This document is the summary of the R for Data Analysis workshop.

All correspondence related to this document should be addressed to:

Omid Ghasemi (Macquarie University, Sydney, NSW, 2109, AUSTRALIA)

Email:

1 Introduction to R

1.1 Basics and Variables

R can be used as a calculator. For mathematical purposes, be careful of the order in which R executes the commands.

10 + 10
## [1] 20
4 ^ 2
## [1] 16
(250 / 500) * 100
## [1] 50

R is a bit flexible with spacing (but no spacing in the name of variables and words)

10+10
## [1] 20
10                 +           10
## [1] 20

R can sometimes tell that you’re not finished yet

10 +

How to create a variable? Variable assignment using <- and =. Note that R is case sensitive for everything

pay <- 250

month = 12

pay * month
## [1] 3000
salary <- pay * month

Few points in naming variables and vectors: use short, informative words, keep same method (e.g., you can use capital letters but it is not recommended, use only _ or . ).

1.2 Function

Function is a set of statements combined together to perform a specific task. When we use a block of code repeatedly, we can convert it to a function. To write a function, first, you need to define it:

my_multiplier <- function(a,b){
  result = a * b
  return (result)
}

This code do nothing. To get a result, you need to call it:

my_multiplier (a=2, b=4)
## [1] 8
# or: my_multiplier (2, 4)

We can set a default value for our arguments:

my_multiplier2 <- function(a,b=4){
  result = a * b
  return (result)
}

my_multiplier2 (a=2)
## [1] 8
# or: my_multiplier (2)
# or: my_multiplier (2, 6)

Fortunately, you do not need to write everything from scratch. R has lots of built-in functions that you can use:

round(54.6787)
## [1] 55
round(54.5787, digits = 2)
## [1] 54.58

Use ? before the function name to get some help. For example, ?round. You will see many functions in the rest of the workshop.

1.3 Data Types

function class() is used to show what is the type of a variable.

  1. Logical: TRUE, FALSE can be abbreviated as T, F. They has to be capital, ‘true’ is not a logical data:
class(TRUE)
## [1] "logical"
class(F)
## [1] "logical"
  1. Numeric: all numbers e.g. 5, 10.5, 11,37; a special type of numeric is “integer” which is numbers without decimal. Integers are always numeric, but numeric is not always integer:
class(2)
## [1] "numeric"
class(13.46)
## [1] "numeric"
  1. Character: text for example, “I love R” or “4” or “4.5”:
class("ha ha ha ha")
## [1] "character"
class("56.6")
## [1] "character"
class("TRUE")
## [1] "character"

Can we change the type of data in a variable? Yes, you need to use the function as.---()

as.numeric(TRUE)
## [1] 1
as.character(4)
## [1] "4"
as.numeric("4.5")
## [1] 4.5
as.numeric("Hello")
## Warning: NAs introduced by coercion
## [1] NA

1.4 Data Structures

1.4.1 Vector

When there are more than one number or letter stored. Use the combine function c() for that.

sale <- c(1, 2, 3,4, 5, 6, 7, 8, 9, 10) # also sale <- c(1:10)

sale <- c(1:10)

sale * sale
##  [1]   1   4   9  16  25  36  49  64  81 100

Subsetting a vector:

days <- c("Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday")

days[2]
## [1] "Sunday"
days[-2]
## [1] "Saturday"  "Monday"    "Tuesday"   "Wednesday" "Thursday"  "Friday"
days[c(2, 3, 4)]
## [1] "Sunday"  "Monday"  "Tuesday"
  • Exercise: Create a vector named my_vector with numbers from 0 to 1000 in it and calculate mean, median, sd, min, max, and sum of that vector:
my_vector <- (0:1000)

mean(my_vector)
## [1] 500
median(my_vector)
## [1] 500
min(my_vector)
## [1] 0
range(my_vector)
## [1]    0 1000
class(my_vector)
## [1] "integer"
sum(my_vector)
## [1] 500500
sd(my_vector)
## [1] 289.1081

1.4.2 List

List allows you to gather a variety of objects under one name (that is, the name of the list) in an ordered way. These objects can be matrices, vectors, data frames, even other list.

my_list = list(sale, 1, 3, 4:7, "HELLO", "hello", FALSE)
my_list
## [[1]]
##  [1]  1  2  3  4  5  6  7  8  9 10
## 
## [[2]]
## [1] 1
## 
## [[3]]
## [1] 3
## 
## [[4]]
## [1] 4 5 6 7
## 
## [[5]]
## [1] "HELLO"
## 
## [[6]]
## [1] "hello"
## 
## [[7]]
## [1] FALSE

1.4.3 Factor

Factors store the vector along with the distinct values of the elements in the vector as labels. The labels are always character irrespective of whether it is numeric or character. For example, variable gender with “male” and “female” entries:

gender <- c("male", "male", "male", " female", "female", "female")
gender <- factor(gender)

R now treats gender as a nominal (categorical) variable: 1=female, 2=male internally (alphabetically).

summary(gender)
##  female  female    male 
##       1       2       3
  • Question: why when we ran the above function i.e. summary(), it showed three and not two levels of the data? Hint: run ‘gender’.
gender
## [1] male    male    male     female female  female 
## Levels:  female female male

So, be careful of spaces!

  • Exercise: Create a gender factor with 30 male and 40 females (Hint: use the rep() function):
gender <- c(rep("male",30), rep("female", 40))
gender <- factor(gender)
gender
##  [1] male   male   male   male   male   male   male   male   male   male  
## [11] male   male   male   male   male   male   male   male   male   male  
## [21] male   male   male   male   male   male   male   male   male   male  
## [31] female female female female female female female female female female
## [41] female female female female female female female female female female
## [51] female female female female female female female female female female
## [61] female female female female female female female female female female
## Levels: female male

There are two types of categorical variables: nominal and ordinal. How to create ordered factors (when the variable is nominal and values can be ordered)? We should add two additional arguments to the factor() function: ordered = TRUE, and levels = c("level1", "level2"). For example, we have a vector that shows participants’ education level.

edu<-c(3,2,3,4,1,2,2,3,4)

education<-factor(edu, ordered = TRUE)
levels(education) <- c("Primary school","high school","College","Uni graduated")
education
## [1] College        high school    College        Uni graduated  Primary school
## [6] high school    high school    College        Uni graduated 
## Levels: Primary school < high school < College < Uni graduated
  • Exercise: We have a factor with patient and control values. Here, the first level is control and the second level is patient. Change the order of levels, so patient would be the first level:
health_status <- factor(c(rep('patient',5),rep('control',5)))
health_status
##  [1] patient patient patient patient patient control control control control
## [10] control
## Levels: control patient
health_status_reordered <- factor(health_status, levels = c('patient','control'))
health_status_reordered
##  [1] patient patient patient patient patient control control control control
## [10] control
## Levels: patient control

Finally, can you relabel both levels to uppercase characters? (Hint: check ?factor)

health_status_relabeled <- factor(health_status, levels = c('patient','control'), labels = c('Patient','Control'))
health_status_relabeled
##  [1] Patient Patient Patient Patient Patient Control Control Control Control
## [10] Control
## Levels: Patient Control

1.4.4 Matrices

All columns in a matrix must have the same mode(numeric, character, etc.) and the same length. It can be created using a vector input to the matrix function.

my_matrix = matrix(c(1,2,3,4,5,6,7,8,9), nrow = 3, ncol = 3)

my_matrix
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9

1.4.5 Data frames

Data frames can hold numeric, character or logical values. Within a column all elements have the same data type, but different columns can be of different data type. Let’s create a dataframe:

id <- 1:200
group <- c(rep("Psychotherapy", 100), rep("Medication", 100))
response <- c(rnorm(100, mean = 30, sd = 5),
             rnorm(100, mean = 25, sd = 5))

my_dataframe <-data.frame(Patient = id,
                          Treatment = group,
                          Response = response)

We also could have done the below

my_dataframe <-data.frame(Patient = c(1:200),
                          Treatment = c(rep("Psychotherapy", 100), rep("Medication", 100)),
                          Response = c(rnorm(100, mean = 30, sd = 5),
                                       rnorm(100, mean = 25, sd = 5)))

In large data sets, the function head() enables you to show the first observations of a data frames. Similarly, the function tail() prints out the last observations in your data set.

head(my_dataframe) 
tail(my_dataframe)
Patient Treatment Response
1 1 Psychotherapy 25.67751
2 2 Psychotherapy 30.50688
3 3 Psychotherapy 27.85405
4 4 Psychotherapy 31.18748
5 5 Psychotherapy 26.43779
6 6 Psychotherapy 29.94332
Patient Treatment Response
195 195 Medication 37.82939
196 196 Medication 29.60533
197 197 Medication 26.16398
198 198 Medication 23.64795
199 199 Medication 31.70907
200 200 Medication 35.42236

Similar to vectors and matrices, brackets [] are used to selects data from rows and columns in data.frames:

my_dataframe[35, 3]
## [1] 29.57839
  • Exercise: How can we get all columns, but only for the first 10 participants?
my_dataframe[1:10, ]
Patient Treatment Response
1 Psychotherapy 25.67751
2 Psychotherapy 30.50688
3 Psychotherapy 27.85405
4 Psychotherapy 31.18748
5 Psychotherapy 26.43779
6 Psychotherapy 29.94332
7 Psychotherapy 31.88499
8 Psychotherapy 24.30275
9 Psychotherapy 29.62321
10 Psychotherapy 25.47421

How to get only the Response column for all participants?

my_dataframe[ , 3]
##   [1] 25.67751 30.50688 27.85405 31.18748 26.43779 29.94332 31.88499 24.30275
##   [9] 29.62321 25.47421 28.67835 44.49822 38.95770 26.71795 32.86743 42.92951
##  [17] 39.43184 25.28679 33.79008 26.63997 30.29671 29.71972 37.59836 25.21335
##  [25] 23.60956 25.40918 32.97853 31.19940 33.52826 23.30590 34.06822 28.86365
##  [33] 28.68556 24.65501 29.57839 33.65160 34.34322 34.88337 33.18487 26.96075
##  [41] 24.85969 30.39736 28.99848 35.35618 33.59227 24.40365 33.69494 39.44119
##  [49] 25.38689 17.62718 29.38772 36.28467 32.72525 32.65730 33.28753 20.59795
##  [57] 29.39656 30.47841 29.72631 30.00521 28.40262 45.39311 33.73790 26.12625
##  [65] 23.75714 36.57074 30.14156 24.95673 27.88779 25.00442 25.27760 25.44098
##  [73] 26.21632 28.23137 38.68072 36.40383 38.53229 30.75424 25.71612 29.67645
##  [81] 23.35774 41.43341 35.42844 27.04721 36.19928 33.90553 31.23478 31.37820
##  [89] 23.47448 35.14562 24.35033 26.32279 28.11685 25.02537 40.15536 28.78169
##  [97] 24.27265 34.10561 32.77914 28.53924 24.44748 25.91238 25.41619 32.77701
## [105] 26.24443 20.20428 14.95122 33.18519 22.92441 23.41872 28.23043 26.70726
## [113] 19.49191 21.57979 21.17556 15.86531 20.19090 28.84964 24.95565 17.15686
## [121] 36.20514 21.61509 29.94009 24.55726 29.75400 29.53863 28.62965 24.30133
## [129] 23.59685 29.22331 20.96769 27.23252 26.04409 28.03235 26.88758 31.41231
## [137] 16.46511 28.84658 22.50181 25.28338 21.68744 21.42948 21.21029 26.07754
## [145] 24.13913 33.76351 24.47093 32.22514 36.42094 23.74251 27.21891 35.39804
## [153] 26.97973 25.25728 14.17212 24.51770 28.84445 19.45879 29.29256 26.29966
## [161] 23.45768 32.36189 29.09744 22.18670 25.16793 16.59150 25.17464 22.56768
## [169] 20.15450 31.92731 24.13743 25.32165 24.07822 24.98594 21.81218 29.17458
## [177] 18.59470 34.26587 31.61718 22.37180 37.62753 21.69863 32.74779 32.97598
## [185] 34.63547 21.96093 24.07853 35.22956 22.56170 23.40207 31.76184 27.97739
## [193] 25.03920 19.07650 37.82939 29.60533 26.16398 23.64795 31.70907 35.42236

Another easier way for selecting particular items is using their names that is more helpful than number of the rows in large data sets:

my_dataframe[ , "Response"]
# OR:
my_dataframe$Response

So far, we created dataframes using data.frame function from the base R. However, a better way to create dataframes is to use the tibble function from tidyverse (see here).

2 Data Cleaning

Now, suppose we ran an experiment with 141 depressed patients. Participants were randomly assigned into two treatment groups: CBT or Psychodynamic psychotherapy. We measured self-report depression scores at 5 different stages of treatment:

  • Stage 1: Before starting any treatment. It is our base stage (pre-test)
  • Stage 2: After 5 sessions of psychotherapy (post-test1)
  • Stage 3: After 10 sessions of psychotherapy (post-test2)
  • Stage 4: At the end of the treatment (post-test3)
  • Stage 5: Three months after the treatment (post-test4)

let’s read and check the uncleaned data. But, first thing first. let’s install and then load the tidyvese package. We also need some other packages:

# Install it
install.packages("tidyverse")

# And then load it
library(tidyverse)

# Load other packages that you have already installed
library(here)
library(janitor)
library(broom)
library(afex)
library(emmeans)
library(knitr)
library(kableExtra)
library(ggsci)
library(patchwork)
library(skimr)
# install.packages("devtools")
# devtools::install_github("easystats/correlation")
library("correlation")
options(scipen=999) # turn off scientific notations
options(contrasts = c('contr.sum','contr.poly')) # set the contrast sum globally 
options(knitr.kable.NA = '')
# read the raw data
raw_data <- read_csv(here("raw_data","raw_data_exp1.csv"))
head(raw_data)
progress subject response_id consent_form age gender stage1_cbt stage2_cbt stage3_cbt stage4_cbt stage5_cbt stage1_dynamic stage2_dynamic stage3_dynamic stage4_dynamic stage5_dynamic anxiety1 anxiety2 anxiety3 anxiety4 anxiety5 anxiety6 anxiety7 anxiety8 group sleep_quality life_satisfaction
100 subj1 R_1f298znjmVzcOjp I consent 18 Female 90 31 33 47 50 5 5 5 5 5 6 5 3 Psychodynamic 9 9
100 subj2 R_tL0A9P33Gi18I0N I consent 18 Male 78 46 46 11 13 6 6 6 5 6 6 5 6 CBT 9 10
100 subj3 R_1LNyJhCKxTAAMOW I consent 19 Female 68 51 24 41 24 6 5 4 5 5 6 5 6 CBT 10 8
100 subj4 R_3enxzUsEYgs5r1a I consent 27 Female 100 21 11 6 31 6 6 6 1 6 6 6 1 Psychodynamic 8 7
100 subj5 R_2Qzl2096a4KNE29 I consent 19 Male 30 28 16 6 6 6 6 5 5 6 6 6 6 CBT 11 11
100 subj6 R_esb71WOTQySjusF I consent 20 Female 79 1 57 46 57 6 6 6 5 6 6 6 6 Psychodynamic 10 10
  • Exercise: There is a dataset in the cleaned_data folder named unicef_u5mr.csv. Read the dataset using read_csv and here.
unicef_data <- read_csv(here("cleaned_data","unicef_u5mr.csv"))

In order to clean the data, we use tidyverse which is a collection of packages to work with data. One of the tidyverse packages that we use regularly is dplyr which includes several functions:

  • mutate() adds new variables or change existing ones.
  • select() pick variables (columns) based on their names.
  • filter() picks cases (rows) based on their values.
  • summarise() gives a single single summary of the data (e.g., mean, counts, etc.)
  • arrange() changes the ordering of the rows.
  • group_by() divides your dataframe into grouped dataframes and allow you to do each of the above operations (except for arrange) on every one of them separately.

2.1 Select

Pick subject, age, and gender columns:

selected_data <- select(raw_data, subject, age, gender)

2.2 Filter

Now, do the following tasks: pick all the male participants, pick all the male participants or those greater than 25 years old, and finally pick all male participants and those greater than 25 years old:

# filter all males
fil_male <- filter(raw_data, gender == "Male")
# filter males and older than 25
fil_male_and_g25 <- filter(raw_data, gender == "Male" & age > 25 )
# filter males or older than 25
fil_male_or_g25 <- filter(raw_data, gender == "Male" | age > 25 )

2.3 Arrange

Arrange (order) your dataframe based on the age, once in an ascending order (youngers first) and once based on descending order (olders first):

# order participants based on their age
arranged_data <- arrange(raw_data, age)
# order participants based on their age (descendeing)
arranged_descending <- arrange(raw_data, desc(age))

2.4 Mutate

Create a column to show if the participant has finished the task or not:

mutated_data <- mutate (raw_data, finished= case_when(progress==100~ "Yes",T~ "No"))

2.5 Summarise

Summarize participants age and sd:

summarise(raw_data, mean= mean(age, na.rm=T),
          sd= sd (age, na.rm=T))
mean sd
21.27273 6.635655

2.6 Pipe Operators

A new function: pipe operators %>% pipes a value into the next function:

raw_data %>% 
  summarise(., mean= mean(age, na.rm=T),
            sd= sd (age, na.rm=T))
mean sd
21.27273 6.635655
raw_data %>% 
  summarise(mean= mean(age, na.rm=T),
            sd= sd (age, na.rm=T))
mean sd
21.27273 6.635655

Calculate the age mean of younger than 25 participants only:

raw_data %>% 
  filter (age < 25) %>%
  summarise(mean= mean(age, na.rm=T),
            sd= sd (age, na.rm=T))
mean sd
19.1913 1.515393

2.7 Group By

Calculate the age mean of younger than 25 participants for each gender separately:

raw_data %>% 
  filter (age < 25) %>%
  group_by(gender) %>%
  summarise(mean= mean(age, na.rm=T),
            sd= sd (age, na.rm=T)) %>%
  ungroup ()
gender mean sd
Female 19.21053 1.556693
Male 19.10000 1.333772
  • Exercise: Create a column to show if participant is older than 23 or not and then calculate sleep quality (sleep_quality) mean for each group separately:
raw_data %>%
  mutate(age_group = case_when(age > 23 ~ "greater than 23", T~ "younger than 23")) %>%
  group_by(age_group) %>%
  summarise(sleep_quality = mean(sleep_quality, na.rm=T))
age_group sleep_quality
greater than 23 9.000000
younger than 23 8.107438
  • Exercise: Add the anxiety total score (sum) to the dataframe and then convert subject column to factor:
anxiety_data <- raw_data %>%
  mutate(anxiety_total= anxiety1+anxiety2+anxiety3+anxiety4+anxiety5+anxiety6+anxiety7+anxiety8) %>%
  mutate(subject= factor(subject))

2.8 Pivoting

Next, we want to pivot our data to switch between long and wide format:

# Make you data long
long_data <- raw_data %>%
  select(subject, stage1_cbt:stage5_cbt,stage1_dynamic:stage5_dynamic) %>%
  pivot_longer(cols = c(stage1_cbt:stage5_dynamic), names_to = 'stage', values_to = 'depression_score')

# Make you data wide
wide_data <- long_data %>%
  pivot_wider(names_from = stage, values_from= depression_score)
  • Exercise: Convert the UNICEF dataset to long and wide formats:
unicef_data <- read_csv(here("cleaned_data","unicef_u5mr.csv"))

library(janitor)
unicef_data_cleaned <- unicef_data %>%
  clean_names()

unicef_long_data <- unicef_data_cleaned %>% pivot_longer(cols = c(u5mr_1950:u5mr_2015), names_to = 'year', values_to = 'u5mr')
unicef_wideg_data <- unicef_long_data %>% pivot_wider(names_from = 'year', values_from = 'u5mr')

Note: The codes for the previous exercise were taken from this blog post written by Simon Ejdemyr.

Now, let’s do some cleaning using dplyr, tidyr and other tidyverse libraries:

cleaned_data <- raw_data %>% 
  filter(progress == 100) %>% # filter out unfinished participants
  select(-consent_form) %>% #remove some useless columns
  # create a total score for our questionnaire
  mutate(anxiety_total= anxiety1+anxiety2+anxiety3+anxiety4+anxiety5+anxiety6+anxiety7+anxiety8) %>%
  select(-anxiety1:-anxiety8) %>%
  # make our dataframe long
  pivot_longer(cols = c(stage1_cbt:stage5_cbt,stage1_dynamic:stage5_dynamic),names_to = 'stage',values_to = 'depression_score') %>% 
  #pivot_wider(names_from = stage, values_from= depression_score) # this code change our dataframe back to wide
  filter(!is.na(depression_score)) %>% #remove rows with depression_score == NA
  mutate(stage= gsub("_.*", "", stage)) %>%
  select (subject, age, gender, group, stage, depression_score, anxiety_total, sleep_quality, life_satisfaction)
subject age gender group stage depression_score anxiety_total sleep_quality life_satisfaction
subj1 18 Female Psychodynamic stage1 90 39 9 9
subj1 18 Female Psychodynamic stage2 31 39 9 9
subj1 18 Female Psychodynamic stage3 33 39 9 9
subj1 18 Female Psychodynamic stage4 47 39 9 9
subj1 18 Female Psychodynamic stage5 50 39 9 9
subj2 18 Male CBT stage1 78 46 9 10

Ok, now the data is clean and tidy which means:

  1. Each variable forms a column.
  2. Each observation forms a row.
  3. Each type of observational unit forms a table (Wickham, 2014).

Check the dataframe and all the data types:

str(cleaned_data)
## tibble [655 × 9] (S3: tbl_df/tbl/data.frame)
##  $ subject          : chr [1:655] "subj1" "subj1" "subj1" "subj1" ...
##  $ age              : num [1:655] 18 18 18 18 18 18 18 18 18 18 ...
##  $ gender           : chr [1:655] "Female" "Female" "Female" "Female" ...
##  $ group            : chr [1:655] "Psychodynamic" "Psychodynamic" "Psychodynamic" "Psychodynamic" ...
##  $ stage            : chr [1:655] "stage1" "stage2" "stage3" "stage4" ...
##  $ depression_score : num [1:655] 90 31 33 47 50 78 46 46 11 13 ...
##  $ anxiety_total    : num [1:655] 39 39 39 39 39 46 46 46 46 46 ...
##  $ sleep_quality    : num [1:655] 9 9 9 9 9 9 9 9 9 9 ...
##  $ life_satisfaction: num [1:655] 9 9 9 9 9 10 10 10 10 10 ...

Finally, we save our data to the cleaned_data folder.

write_csv(cleaned_data, here("cleaned_data","cleaned_data_exp1.csv"))

3 Data Visualization

Before starting the ggplot, let’s try a visualization using a function from the Base R the plot() function shows the association of each variable against the other one in a data handy for data with few number of variables to see if there are any patterns

exam_data<- read_csv(here::here("cleaned_data", "exam_data.csv"))

plot(x = exam_data$Anxiety, y = exam_data$Exam)

The code also works without writing x and y, however, writing them is strongly recommended

plot(exam_data$Anxiety, exam_data$Exam)

ggplot, the gg in ggplot stands for grammar of graphics. Grammar of graphics basically says any graphical representation of data, can be produced by a series of layers. You can think of a layer as a plastic transparency. Lets draw the same plot using ggplot. Always, mention the data we are going to work with.

ggplot(data = exam_data, aes(x = Exam, y = Anxiety))

  • aes: aes which stands for aesthetics is a relationship between a variable in your dataset and an aspect of the plot that is going to visually convey the information to the reader

  • Visual elements are known as geoms (short for ‘geometric objects’) in ggplot 2. When we define a layer, we have to tell R what geom we want displayed on that layer (do we want a bar, line dot, etc.?)

ggplot(data = exam_data, aes(x = Exam, y = Anxiety))+ geom_point()

So, lets try some of them here like shape and size. Be careful with the + sign, if you clink enter for the next part of the code, the + sign should not go to the next line

ggplot(data = exam_data, aes(x = Exam, y = Anxiety))+
  geom_point(size = 2, shape = 8)

The current plot is not very informative about the patterns for each gender.

ggplot(data = exam_data, aes(x = Exam, y = Anxiety, color = Gender))+
  geom_point(size = 2, shape = 10)

ggplot(data = exam_data, aes(x = Exam, y = Anxiety, color = Gender, shape = Gender))+
  geom_point(size = 2, shape = 10)

Question: why the above code doesn’t make any change?

ggplot(data = exam_data, aes(x = Exam, y = Anxiety, color = Gender, shape = Gender))+
  geom_point(size = 2)

Can assign the first layer to a variable to reduce the length of codes for next layers.

My_graph <- ggplot(data = exam_data, aes(x = Exam, y = Anxiety))

My_graph + geom_point()

lets add a line to the current graph

My_graph + geom_point() + geom_smooth()

Aesthetics can be set for all layers of the plot (i.e., defined in the plot as a whole) or can be set individually for each geom in a plot.

My_graph + geom_point(aes(color = Gender)) + geom_smooth()

My_graph + geom_point(aes(color = Gender)) + geom_smooth(aes(color = Gender))

The shaded area around the line is the 95% confidence interval around the line. We can switch this off by adding se = F (which is short for ‘standard error = False’)

My_graph + geom_point() + geom_smooth(se = F)

What if we want our line to be a direct line?

My_graph + geom_point() + geom_smooth(se = F, method = lm)

How to change the labels of x and y axes?

My_graph + geom_point() + geom_smooth(se = F, method = lm) +
  labs(x = "Exam scores %", y = "Anxiety scores")

Histograms are used to show distributions of variables while bar charts are used to compare variables. Histograms plot quantitative data with ranges of the data grouped into bins or intervals while bar charts plot categorical data.

#ggplot(data = exam_data, aes(x = Anxiety, y = Exam )) + geom_histogram()
# the code above gives an error as geom_histogram can only have x or y axis in its aes()

ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram()

ggplot(data = exam_data, aes(y = Anxiety)) + geom_histogram()

ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram(bins = 31)

ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram(bins = 31, fill = "green")

ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram(bins = 31, fill = "green", col = "red")

Let’s stop using the My_graph variable and write the whole code from the start again for a bar chart

ggplot(data = exam_data, aes(x = Sleep_quality))+
  geom_bar()

Because we want to plot a summary of the data (the mean) rather than the raw scores themselves, we have to use a stat.

ggplot(data = exam_data, aes(x = Sleep_quality, y = Exam, fill = Gender))+
  geom_bar(stat = "summary", fun = "mean")

ggplot(data = exam_data, aes(x = Sleep_quality, y = Exam, fill = Gender))+
  geom_bar(stat = "summary", fun = "mean", position = "dodge")

The other way to get the same plot that the code above gives, is using the stat_summary function that takes the following general form: stat_summary(function = x, geom = y)

ggplot(data = exam_data, aes(x = Sleep_quality, y = Exam, fill = Gender))+
  stat_summary(fun = mean, geom = "bar", position = "dodge")

How to combine multiple plots? How to combine multiple plots? We can use the patchwork package. A nice tutorial on using this package can be found here

p1 = My_graph + geom_point(aes(color = Gender)) + geom_smooth()

p2 = ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram(bins = 31)

p3 = ggplot(data = exam_data, aes(x = Sleep_quality, y = Exam, fill = Gender))+
  stat_summary(fun = mean, geom = "bar", position = "dodge")

p4 = My_graph + geom_point() + geom_smooth(se = F, method = lm) +
  labs(x = "Exam scores %", y = "Anxiety scores")

combined = p1 + p2+ p3 + p4 + plot_layout(nrow = 4, byrow = F)

combined

p1 | p2 / p3 / p4

p1 | p2 / (p3 / p4)

ggsave() function, which is a versatile exporting function that can export as PostScript (.eps/.ps), tex (pictex), pdf, jpeg, tiff, png, bmp, svg and wmf (in Windows only). In its basic form, the structure of the function is very simple: ggsave(filename)

ggsave(combined, filename = here("outputs", "combined.png"), dpi=300)

Now that we learned the basics of ggplot, let’s draw some plot for our experiment data. First, we need to create a dataset with aggregated depression_score scores over group and stage. We will use this dataset for line and bar graphs.

library(ggsci)

data_exp1_orig <- read_csv(here("cleaned_data","cleaned_data_exp1.csv"))

data_exp1 <- data_exp1_orig%>% 
  #mutate_if(is.character, factor) %>%
  mutate(subject= factor(subject), # convert all characters to factor
         group = factor(group),
         stage = factor(stage))


aggregated_data_exp1 <- data_exp1 %>%
  group_by(stage, group) %>%
  mutate(depression_score = mean(depression_score)) %>%
  ungroup()


barplot_exp1 <- aggregated_data_exp1 %>%
  ggplot(aes(x=stage, y= depression_score, fill=group)) +
  geom_bar(stat = "identity", position= "dodge")+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  scale_fill_jama() 

#ggsave(barplot_exp1, filename = here("outputs","barplot_exp1.png"), dpi=300)


barplot_facet_exp1 <- aggregated_data_exp1 %>%
  ggplot(aes(x=group, y= depression_score, fill=stage)) +
  geom_bar(stat = "identity", position= "dodge")+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  theme(legend.position = "none",
        axis.text=element_text(size=11),
        axis.title = element_text(size = 12)) +
  facet_wrap(~stage, nrow = 1)+
  scale_fill_jco() 

#ggsave(barplot_facet_exp1, filename = here("outputs","barplot_facet_exp1.png"), dpi=300)


lineplot_exp1 <- aggregated_data_exp1 %>%
  ggplot(aes(x=factor(stage), y= depression_score, group= group, color= group)) +
  geom_line(aes(linetype= group)) +
  geom_point(size= 5)+
  labs (x= '', y= "Depression Score") + 
  theme_classic() +
  theme(legend.position = "bottom",
        axis.text=element_text(size=11),
        axis.title = element_text(size = 12)) +
  scale_color_nejm() 

#ggsave(lineplot_exp1, filename = here("outputs","lineplot_exp1.png"), dpi=300)


violinplot_exp1 <- data_exp1 %>%
  ggplot(aes(x=factor(stage), y= depression_score, fill= group)) +
  geom_violin()+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  theme(legend.position = "bottom",
        axis.text=element_text(size=11)) +
  scale_fill_d3() 

#ggsave(violinplot_exp1, filename = here("outputs","violinplot_exp1.png"), dpi=300)


boxplot_exp1 <- data_exp1 %>%
  ggplot(aes(x=factor(stage), y= depression_score, fill= group)) +
  geom_boxplot()+
  #geom_point(position = position_dodge(width=0.75), alpha= .5)+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  theme(legend.position = "bottom",
        axis.text=element_text(size=11)) +
  scale_fill_simpsons() 

#ggsave(boxplot_exp1, filename = here("outputs","boxplot_exp1.png"), dpi=300)


boxplot_facet_exp1 <- data_exp1 %>%
  ggplot(aes(x=factor(stage), y= depression_score, fill= group)) +
  geom_boxplot()+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  theme(legend.position = "bottom",
        axis.text=element_text(size=11),
        axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
  facet_wrap(~group)+
  scale_color_simpsons() 

#ggsave(boxplot_facet_exp1, filename = here("outputs","boxplot_facet_exp1.png"), dpi=300)

Let’s combine our plots:

combined_plot_exp1 <- barplot_facet_exp1 / (lineplot_exp1+violinplot_exp1+boxplot_exp1)
combined_plot_exp1

And here, we save our plots to the outputs folder.

ggsave(combined_plot_exp1, filename = here("outputs","combined_plot_exp1.png"), dpi=300, width = 12)

4 Descriptive Statistics

Now, let’s do some descriptive statistics. Now, we can open a new script called data_analysis.r and read some datasets. Then we use skimr package to describe our data.

narcissism_data <- read_csv(here("cleaned_data","narcissism_data.csv"))
narcissism_data %>% skimr::skim()
Data summary
Name Piped data
Number of rows 131
Number of columns 5
_______________________
Column type frequency:
character 1
numeric 4
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
subject 0 1 5 7 0 131 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
psychopathy 0 1 8.78 2.27 0 8.0 10 10 11 ▁▁▁▂▇
self_esteem 0 1 8.45 1.68 4 8.0 8 9 12 ▁▅▇▆▃
narcissism 0 1 38.20 6.15 19 33.5 39 43 48 ▁▂▇▇▆
mental_health 0 1 3.19 1.04 1 3.0 4 4 4 ▂▂▁▃▇
  • Exercise: Open the dataset called treatment_data.csv and do a descriptive analysis:
treatment_data <- read_csv(here("cleaned_data","treatment_data.csv"))
treatment_data %>% skimr::skim()
Data summary
Name Piped data
Number of rows 131
Number of columns 7
_______________________
Column type frequency:
character 3
numeric 4
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
subject 0 1 5 7 0 131 0
gender 0 1 4 6 0 2 0
treatment 0 1 3 13 0 2 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
age 0 1 21.15 6.52 16 18.0 19 20.0 63 ▇▁▁▁▁
anxiety 0 1 62.35 24.51 0 40.0 69 81.0 100 ▂▆▃▇▆
depression 0 1 52.50 22.12 0 34.5 51 71.0 100 ▂▇▇▆▃
life_satisfaction 0 1 41.02 23.93 0 21.0 39 56.5 100 ▅▇▅▃▂
  • Exercise: Do the same thing for the memory_data.csv.
memory_data <- read_csv(here("cleaned_data","memory_data.csv"))
memory_data %>% group_by(time) %>%
  skimr::skim()
Data summary
Name Piped data
Number of rows 262
Number of columns 5
_______________________
Column type frequency:
character 2
numeric 2
________________________
Group variables time

Variable type: character

skim_variable time n_missing complete_rate min max empty n_unique whitespace
subject post_test_memory 0 1 5 7 0 131 0
subject pre_test_memory 0 1 5 7 0 131 0
gender post_test_memory 0 1 4 6 0 2 0
gender pre_test_memory 0 1 4 6 0 2 0

Variable type: numeric

skim_variable time n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
age post_test_memory 0 1 21.15 6.52 16 18.0 19 20.0 63 ▇▁▁▁▁
age pre_test_memory 0 1 21.15 6.52 16 18.0 19 20.0 63 ▇▁▁▁▁
memory_score post_test_memory 0 1 52.50 22.12 0 34.5 51 71.0 100 ▂▇▇▆▃
memory_score pre_test_memory 0 1 41.02 23.93 0 21.0 39 56.5 100 ▅▇▅▃▂
  • Exercise: For this exercise, we use a dataset of one of my own studies. In this study, we asked participants to guess the physical brightness of reasoning arguments and then we gave a cognitive ability test. (See the original study here). Open ghasemi_brightness_exp4.csv file and answer to the following questions:
  1. How many participants did we test in total?
  2. Find out how many male and female we tested.
  3. Calculate mean and sd for age and cognitive ability (cog_ability).
ghasemi_data <- read_csv(here("cleaned_data","ghasemi_brightness_exp4.csv"))

ghasemi_data %>% summarise(n = n_distinct(participant)) # number of participants:200
n
200
ghasemi_data %>% group_by (participant) %>% filter (row_number()==1) %>% group_by (gender) %>% summarise(n= n()) %>% ungroup() # 183 female, 17 male
gender n
Female 183
Male 17
ghasemi_data %>% dplyr::select (age, cog_ability) %>% skimr::skim() # mean and sd for age and cognitive ability
Data summary
Name Piped data
Number of rows 38400
Number of columns 2
_______________________
Column type frequency:
numeric 2
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
age 0 1 22.20 6.78 17 19 20 22 52 ▇▁▁▁▁
cog_ability 0 1 39.55 9.46 11 34 40 46 61 ▁▃▇▆▂

5 Data Analysis

5.1 t-test

Now, we use the treatment data to run three different independent t-tests. Suppose we did an experiment to compare the effectiveness of CBT vs. Psychodynamic therapies in decreasing anxiety, and depression and also in improving life satisfaction:

# t.test (indep)
t.test(anxiety~treatment, data= treatment_data)
## 
##  Welch Two Sample t-test
## 
## data:  anxiety by treatment
## t = -0.85021, df = 124.18, p-value = 0.3968
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -12.11096   4.83264
## sample estimates:
##           mean in group CBT mean in group Psychodynamic 
##                    60.54545                    64.18462
t.test(depression~treatment, data= treatment_data)
## 
##  Welch Two Sample t-test
## 
## data:  depression by treatment
## t = -2.8725, df = 123.97, p-value = 0.004792
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -18.21965  -3.35424
## sample estimates:
##           mean in group CBT mean in group Psychodynamic 
##                    47.15152                    57.93846
t.test(life_satisfaction~treatment, data= treatment_data)
## 
##  Welch Two Sample t-test
## 
## data:  life_satisfaction by treatment
## t = -5.2688, df = 127.11, p-value = 0.0000005699
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -27.61850 -12.53721
## sample estimates:
##           mean in group CBT mean in group Psychodynamic 
##                    31.06061                    51.13846

In another experiment, suppose we have created a method to boost memory. Then, we recruit some participants, do a memory pre-test, implement the method, and do a memory post-test, Now, we want to see whether our method have improved participants’ memory:

# t.test (paired)
t.test(memory_score~time, data= memory_data, paired= T)
## 
##  Paired t-test
## 
## data:  memory_score by time
## t = 5.4761, df = 130, p-value = 0.0000002163
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##   7.333171 15.628661
## sample estimates:
## mean of the differences 
##                11.48092

Now that we learned about t-test, let’s perform this test on our dataset. Is there a difference between groups at the first stage? Ideally, we want participants’ depresion score at the first stage to be similar for both groups because we have not started our treatment yet. Previous graphs showed us that depression scores of the CBT and Psychodynamic groups at this stage are pretty close. Let’s test that using an independent t-test (because we have 2 independent groups):

# Is there a difference between groups at the first stage?
data_exp1 %>% 
  group_by(group) %>% 
  filter(stage=='stage1') %>% 
  ungroup () %>%
  t.test(depression_score~group, data = ., paired=FALSE)
## 
##  Welch Two Sample t-test
## 
## data:  depression_score by group
## t = 0.10768, df = 118.92, p-value = 0.9144
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -9.205588 10.264329
## sample estimates:
##           mean in group CBT mean in group Psychodynamic 
##                    53.59091                    53.06154

Now, we wonder if psychotherapy treatments were effective at all, regardless of the treatment method. So, we would like to test if depresion score at the forth stage are lower than scores at the stage 2? Since a pair of score at stage 2 and stage 4 is coming from a same person, we use paired t-test.

# Is there a difference between ratings of stage2 and stage4?
data_exp1 %>% 
  filter(stage=='stage2' | stage=='stage4') %>% 
  ungroup () %>%
  t.test(depression_score~stage, data = ., paired=TRUE)
## 
##  Paired t-test
## 
## data:  depression_score by stage
## t = 5.5931, df = 130, p-value = 0.0000001261
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##   7.70108 16.13098
## sample estimates:
## mean of the differences 
##                11.91603
  • Exercise: John et al. (2019) investigated the consequences of backing down (changing one’s mind in lights of evidence)and how other people view someone who change their mind. In their second experiments, they presented participants either with a person who changes their mind or a person who refuses to back down. Then, they asked participants to rate how intelligent and confident the person is (See the original study here). They reported that:

“Relative to the entrepreneur who did not back down, participants judged the entrepreneur who backed down as more intelligent (M_backed_down=5.13 out of 7, SD=1.09; M_did_not_back_down=3.97, SD=1.54; t(271.12)=−7.59, p < .001) but less confident (M_backed_down=4.50 out of 7, SD=1.36; M_did_not_back_down=5.65, SD=1.10; t(291.01)=8.08, p < .001).”.

Open the john_backdown_exp2.csv file and try to reproduce their results. Run two separate independent t-test, one with intelligent as the dependent variable and one with confident as the dependent variable. For both t-test, use back_down as the between-subject independent variable.

john_data <- read_csv(here("cleaned_data","john_backdown_exp2.csv"))


t.test(intelligent~back_down, data = john_data, paired=FALSE)
## 
##  Welch Two Sample t-test
## 
## data:  intelligent by back_down
## t = 7.5853, df = 271.12, p-value = 0.0000000000005319
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.8577107 1.4590076
## sample estimates:
##       mean in group backed_down mean in group did_not_back_down 
##                        5.129412                        3.971053
t.test(confident~back_down, data = john_data, paired=FALSE)
## 
##  Welch Two Sample t-test
## 
## data:  confident by back_down
## t = -8.0763, df = 291.01, p-value = 0.00000000000001787
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -1.4257768 -0.8670294
## sample estimates:
##       mean in group backed_down mean in group did_not_back_down 
##                        4.503268                        5.649671

5.2 Analysis of Variance (ANOVA)

Now, let’s analysis our main experiment data: Do participants in the CBT group show better outcome compared to participants in the Psychodynamic group? Suppose we believe that participants should show lower depression after 5 or 10 sessions of both psychotherapy treatments and this decrease should be more pronounced for CBT than psychodynamic psychotherapy. If this is the case. we expect an interaction in the traditional Analysis of Variance (AONVA) test.

aov_m1 <- aov_car (depression_score ~ group*stage +
                     Error(subject/stage), data = data_exp1)  
Effect df MSE F ges p.value
group 1, 129 737.60 27.08 *** .066 <.001
stage 2.97, 382.72 492.81 53.15 *** .215 <.001
group:stage 2.97, 382.72 492.81 8.91 *** .044 <.001

As you can see, we found a significant main effect of stage and a significant group by stage interaction. We can use the emmeans package to do post-hoc tests.

# main effect of stage
emmeans(aov_m1, 'stage')
##  stage  emmean   SE  df lower.CL upper.CL
##  stage1   53.3 1.83 579     49.7     56.9
##  stage2   33.3 1.83 579     29.7     36.9
##  stage3   26.3 1.83 579     22.7     29.9
##  stage4   21.4 1.83 579     17.8     25.0
##  stage5   31.4 1.83 579     27.8     35.0
## 
## Results are averaged over the levels of: group 
## Warning: EMMs are biased unless design is perfectly balanced 
## Confidence level used: 0.95
pairs(emmeans(aov_m1, 'stage'), adjust= 'holm')
##  contrast        estimate   SE  df t.ratio p.value
##  stage1 - stage2    20.03 2.36 516  8.480  <.0001 
##  stage1 - stage3    26.94 2.36 516 11.404  <.0001 
##  stage1 - stage4    31.91 2.36 516 13.506  <.0001 
##  stage1 - stage5    21.84 2.36 516  9.245  <.0001 
##  stage2 - stage3     6.91 2.36 516  2.924  0.0144 
##  stage2 - stage4    11.87 2.36 516  5.027  <.0001 
##  stage2 - stage5     1.81 2.36 516  0.766  0.4442 
##  stage3 - stage4     4.97 2.36 516  2.102  0.0941 
##  stage3 - stage5    -5.10 2.36 516 -2.158  0.0941 
##  stage4 - stage5   -10.07 2.36 516 -4.261  0.0001 
## 
## Results are averaged over the levels of: group 
## P value adjustment: holm method for 10 tests
# group by stage interaction
emmeans(aov_m1, "group", by= "stage")
## stage = stage1:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             53.5 2.59 577    48.47     58.6
##  Psychodynamic   53.0 2.60 580    47.92     58.1
## 
## stage = stage2:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             30.7 2.59 577    25.58     35.7
##  Psychodynamic   35.9 2.60 580    30.75     41.0
## 
## stage = stage3:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             21.7 2.59 577    16.62     26.8
##  Psychodynamic   31.0 2.60 580    25.89     36.1
## 
## stage = stage4:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             13.4 2.59 577     8.29     18.4
##  Psychodynamic   29.4 2.60 580    24.29     34.5
## 
## stage = stage5:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             18.8 2.59 577    13.74     23.9
##  Psychodynamic   44.1 2.60 580    38.96     49.2
## 
## Warning: EMMs are biased unless design is perfectly balanced 
## Confidence level used: 0.95
update(pairs(emmeans(aov_m1, "group", by= "stage")), by = NULL, adjust = "holm") 
##  contrast            stage  estimate   SE  df t.ratio p.value
##  CBT - Psychodynamic stage1    0.529 3.67 579  0.144  0.8852 
##  CBT - Psychodynamic stage2   -5.195 3.67 579 -1.417  0.3138 
##  CBT - Psychodynamic stage3   -9.288 3.67 579 -2.534  0.0346 
##  CBT - Psychodynamic stage4  -16.022 3.67 579 -4.371  0.0001 
##  CBT - Psychodynamic stage5  -25.244 3.67 579 -6.887  <.0001 
## 
## P value adjustment: holm method for 5 tests

You can use the afex_plot function from afex to create beautiful plots. Those plots interacts nicely with ggplot:

afex_plot(aov_m1, x = "stage", trace = "group", error='between',
          line_arg = list(size=1),
          point_arg = list(size=3.5),
          data_arg = list(size= 1, color= 'grey', width=.4),
          data_geom = geom_boxplot,
          mapping = c("linetype", "shape", "fill"),
          legend_title = "Group") +
  labs(y = "Depression Score", x = "") +
  theme_bw()+ # remove the grey background and grid
  theme(axis.text=element_text(size=13),
        axis.title = element_text(size = 13),
        legend.text=element_text(size=13),
        legend.title=element_text(size=13),
        legend.position='bottom',
        legend.key.size = unit(1, "cm"),
        legend.background = element_rect(colour = 'black', fill = 'white', linetype='solid'))+
  scale_color_simpsons() +
  scale_fill_simpsons()

If you are interested in this topic, check out this nice tutorial about using afex to run ANOVA, and also this interesting tutorial on the emmeans package.

  • Exercise: Rotello et al. (2018) investigated the association between the race (White vs. Black faces) and the gun-tool judgments. In their first experiments, they presented participants with 16 White male faces and 16 Black male faces, and following that 8 images of guns and 8 images of tools. They asked participants to judge if the object is a tool or a gun by pressing keyboard buttons. Then, they ran an ANOVA to see if participants’ gun responses are higher for any of the races. So, they included prime race (Black, White) and target identity (gun, tool) as independent variables and participants’ gun responses as dependent variable into their linear model (See the original study here). They found that:

“Participants made more gun responses to guns than to tools, F(1,45) = 53243, p < 0.0001, η2g = 0.998. However, the race of the prime face did not matter, F(1,45) = 0.287, p > 0.59, η2g = 0.001, nor was there an interaction of prime race with target object, F(1,45) = 0.022, p > 0.88, η2g = 0.000)”.

Open the rotello_shooter_exp1.csv file and try to reproduce their results. Run an ANOVA (type III) with resp as the dependent variable and target, prime, and their interaction as independent variables.

# load the general data file
rotello_data <- read_csv(here("cleaned_data","rotello_shooter_exp1.csv"))

# ANOVA
rotello_aov <- aov_car (resp ~ target*prime +
           Error(subject/target*prime), data = rotello_data)
Effect df MSE F ges p.value
target 1, 45 0.00 53242.99 *** .998 <.001
prime 1, 45 0.00 0.29 .001 .595
target:prime 1, 45 0.00 0.02 <.001 .883

5.3 Correlation

Here, we want to check the correlation between variables on the narcissism_data. First, we need to remove subject column because it is not numeric:

narcissism_data_cor <- narcissism_data %>%
  select(-subject)
#-- Base R:
cor(narcissism_data_cor, method = "pearson",  use = "complete.obs")

#-- Psych library:
psych::pairs.panels(narcissism_data_cor, method = "pearson", hist.col = "#00AFBB", density = T, ellipses = F, stars = T)

#-- Correlation library:
# install.packages("devtools")
# devtools::install_github("easystats/correlation")
#library("correlation")
correlation::correlation(narcissism_data_cor) %>% summary()

#-- apaTables library:
narcissism_data_cor %>% 
  apaTables::apa.cor.table(filename="./outputs/CorMatrix.doc", show.conf.interval=T)
psychopathy self_esteem narcissism mental_health
psychopathy 1.00 0.15 0.40 -0.44
self_esteem 0.15 1.00 0.11 -0.29
narcissism 0.40 0.11 1.00 -0.26
mental_health -0.44 -0.29 -0.26 1.00
Parameter mental_health narcissism self_esteem
psychopathy -0.44 0.40 0.15
self_esteem -0.29 0.11
narcissism -0.26
  • Exercise: Pennycook et al. (2020) investigated the relationship between actively open-minded thinking style about evidence (AOT-E) and different political, scientific, and religious beliefs (see the original paper here). In their first experiment, they calculated the correlation of AOTE and scientific beliefs items (global warming, evolution, etc.) and they found the following results:

Open the pennycook_aote_exp1.csv file and try to reproduce their results by creating the same correlation matrix.

pennycook_data <- read_csv(here("cleaned_data","pennycook_aote_exp1.csv")) 


#---------- Base R:
cor(pennycook_data, method = "pearson",  use = "complete.obs")

#---------- Psych library:
pennycook_data %>% 
  psych::pairs.panels(method = "pearson", hist.col = "#00AFBB", density = T, ellipses = F, stars = T)

#---------- Correlation library:
correlation::correlation(pennycook_data) %>% summary()

#---------- apaTables library:
pennycook_data %>% 
  apaTables::apa.cor.table(filename="./outputs/CorMatrix.doc", show.conf.interval=T)
Parameter trust_scien gm_health tech_problems modern_medicine old_earth vaccines stem_cell big_bang evolution global_warming
aote 0.35 0.36 0.44 0.33 0.40 0.47 0.45 0.51 0.51 0.37
global_warming 0.42 0.06 0.14 0.18 0.33 0.26 0.31 0.33 0.38
evolution 0.48 0.33 0.28 0.36 0.47 0.39 0.54 0.78
big_bang 0.49 0.37 0.28 0.36 0.45 0.37 0.54
stem_cell 0.47 0.34 0.36 0.47 0.40 0.40
vaccines 0.43 0.52 0.49 0.53 0.38
old_earth 0.29 0.24 0.21 0.33
modern_medicine 0.43 0.42 0.47
tech_problems 0.33 0.39
gm_health 0.31

5.4 Linear Regression

Here, we do single and multiple linear regreassion on the narcissism_data:

m1 <- lm(mental_health~narcissism, data= narcissism_data)
term estimate std.error statistic p.value
(Intercept) 4.86 0.56 8.75 0
narcissism -0.04 0.01 -3.04 0
m2 <- lm(mental_health~narcissism+psychopathy, data= narcissism_data)
term estimate std.error statistic p.value
(Intercept) 5.43 0.53 10.27 0.00
narcissism -0.02 0.01 -1.09 0.28
psychopathy -0.19 0.04 -4.71 0.00
  • Exercise: Trémolière and Djeriouat (2020) examined the role of cognitive reflection and belief in science in climate change skepticism. In their first study, they revealed that cognitive reflection and belief in science negetively predicted climate change skepticism even after controlling for demographic and cognitive ability variables (see the original paper here).

Open the tremoliere_data_exp1.csv file and try to reproduce their results by running a multiple linear regression. Enter age, gender, education, belief in science, literacy, numeracy (Numtotal), and cognitive reflection as predictors and enter climate change skepticism (climato) as the outcome variable.

Tremoliere_data <- read_csv(here("cleaned_data","tremoliere_data_exp1.csv"))

Tremoliere_reg=lm(Climato ~ Age+ Gender+ Education+ BeliefInSciencetotal+ Literacy+ Numtotal+ CognitiveReflection,
                    data=Tremoliere_data)
term estimate std.error statistic p.value
(Intercept) 57.57 5.19 11.09 0.00
Age 0.01 0.05 0.24 0.81
Gender -5.68 1.34 -4.23 0.00
Education 0.54 0.38 1.43 0.15
BeliefInSciencetotal -0.20 0.06 -3.62 0.00
Literacy -0.49 0.51 -0.96 0.34
Numtotal -1.52 0.83 -1.82 0.07
CognitiveReflection -18.58 4.26 -4.37 0.00
r.squared adj.r.squared sigma statistic p.value df logLik AIC BIC deviance df.residual nobs
0.19 0.17 12.65 11.91 0 7 -1467.77 2953.54 2988.81 58235.89 364 372

6 Rmarkdown

To be completed…

7 References

  • Ghasemi, O., Handley, S., & Howarth, S. (2020). The Bright Homunculus in our Head: Individual Differences in Intuitive Sensitivity to Logical Validity.

  • John, L. K., Jeong, M., Gino, F., & Huang, L. (2019). The self-presentational consequences of upholding one’s stance in spite of the evidence. Organizational Behavior and Human Decision Processes, 154, 1-14.

  • Pennycook, G., Cheyne, J. A., Koehler, D. J., & Fugelsang, J. A. (2020). On the belief that beliefs should change according to evidence: Implications for conspiratorial, moral, paranormal, political, religious, and science beliefs. Judgment and Decision Making, 15(4), 476.

  • Rotello, C. M., Kelly, L. J., Heit, E., Vazire, S., & Vul, E. (2018). The Shape of ROC Curves in Shooter Tasks: Implications for Best Practices in Analysis. Collabra: Psychology, 4(1).

  • Trémolière, B., & Djeriouat, H. (2020). Don’t you see that its cold! Exploring the roles of cognitive reflection, climate science literacy, illusion of knowledge, and political orientation in climate change skepticism.

  • Wickham, H. (2014). Tidy data. Journal of Statistical Software, 59(10), 1-23.

LS0tCnRpdGxlOiAiUiBmb3IgRGF0YSBBbmFseXNpcyIKYXV0aG9yOgogIC0gbmFtZTogIk9taWQgR2hhc2VtaSIKICAgIGFmZmlsaWF0aW9uOiBNYWNxdWFyaWUgVW5pdmVyc2l0eQogICAgZW1haWw6IG9taWRyZXphLmdoYXNlbWlAaGRyLm1xLmVkdS5hdQogIC0gbmFtZTogIk1haGRpIE1hemlkaSIKICAgIGFmZmlsaWF0aW9uOiBVbml2ZXJzaXR5IG9mIFdlc3Rlcm4gQXVzdHJhbGlhCiAgICBlbWFpbDogbWFoZGkubWF6aWRpc2hhcmFmYWJhZGlAcmVzZWFyY2gudXdhLmVkdS5hdQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGtlZXBfbWQ6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0aGVtZTogY2VydWxlYW4KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgICNjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogImthYmxlIgotLS0KClRoaXMgZG9jdW1lbnQgaXMgdGhlIHN1bW1hcnkgb2YgdGhlICoqUiBmb3IgRGF0YSBBbmFseXNpcyoqIHdvcmtzaG9wLiAKCkFsbCBjb3JyZXNwb25kZW5jZSByZWxhdGVkIHRvIHRoaXMgZG9jdW1lbnQgc2hvdWxkIGJlIGFkZHJlc3NlZCB0bzogCgo8Y2VudGVyPgpPbWlkIEdoYXNlbWkgKE1hY3F1YXJpZSBVbml2ZXJzaXR5LCBTeWRuZXksIE5TVywgMjEwOSwgQVVTVFJBTElBKSAKCkVtYWlsOiBvbWlkcmV6YS5naGFzZW1pQGhkci5tcS5lZHUuYXUgCjwvY2VudGVyPgoKCgo8c3R5bGU+Cgpib2R5eyAvKiBOb3JtYWwgICovCiAgICAgIHRleHQtYWxpZ246IGp1c3RpZnk7CiAgICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwp9CmNvZGUucnsgLyogQ29kZSBibG9jayAqLwogICAgZm9udC1zaXplOiAxNHB4Owp9CnByZSB7IC8qIENvZGUgYmxvY2sgLSBkZXRlcm1pbmVzIGNvZGUgc3BhY2luZyBiZXR3ZWVuIGxpbmVzICovCiAgICBmb250LXNpemU6IDEycHg7Cn0KCjwvc3R5bGU+CgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5hbGlnbj0iY2VudGVyIikKYGBgCgoKCmBgYHtyIGxpYnJhcmllcywgbWVzc2FnZT1GQUxTRSwgZWNobz1GfQojIGxvYWQgbGlicmFyaWVzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShicm9vbSkKbGlicmFyeShhZmV4KQpsaWJyYXJ5KGVtbWVhbnMpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShnZ3NjaSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoc2tpbXIpCiMgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiZWFzeXN0YXRzL2NvcnJlbGF0aW9uIikKbGlicmFyeSgiY29ycmVsYXRpb24iKQpvcHRpb25zKHNjaXBlbj05OTkpICMgdHVybiBvZmYgc2NpZW50aWZpYyBub3RhdGlvbnMKb3B0aW9ucyhjb250cmFzdHMgPSBjKCdjb250ci5zdW0nLCdjb250ci5wb2x5JykpICMgc2V0IHRoZSBjb250cmFzdCBzdW0gZ2xvYmFsbHkgCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykKYGBgCgoKIyBJbnRyb2R1Y3Rpb24gdG8gUgoKIyMgQmFzaWNzIGFuZCBWYXJpYWJsZXMKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSI3MDBweCIsIGZpZy5jYXA9ICJBcnR3b3JrIGJ5IEFsbGlzb24gSG9yc3Q6IGh0dHBzOi8vZ2l0aHViLmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucyJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ3JfZmlyc3RfdGhlbi5wbmcnKSkKYGBgCgoKUiBjYW4gYmUgdXNlZCBhcyBhIGNhbGN1bGF0b3IuIEZvciBtYXRoZW1hdGljYWwgcHVycG9zZXMsIGJlIGNhcmVmdWwgb2YgdGhlIG9yZGVyIGluIHdoaWNoIFIgZXhlY3V0ZXMgdGhlIGNvbW1hbmRzLgoKYGBge3J9CjEwICsgMTAKCjQgXiAyCgooMjUwIC8gNTAwKSAqIDEwMApgYGAKClIgaXMgYSBiaXQgZmxleGlibGUgd2l0aCBzcGFjaW5nIChidXQgbm8gc3BhY2luZyBpbiB0aGUgbmFtZSBvZiB2YXJpYWJsZXMgYW5kIHdvcmRzKQoKYGBge3J9CjEwKzEwCgoxMCAgICAgICAgICAgICAgICAgKyAgICAgICAgICAgMTAKYGBgCgpSIGNhbiBzb21ldGltZXMgdGVsbCB0aGF0IHlvdSdyZSBub3QgZmluaXNoZWQgeWV0CgpgYGB7ciBldmFsPUZ9CjEwICsKYGBgCgpIb3cgdG8gY3JlYXRlIGEgKnZhcmlhYmxlKj8gVmFyaWFibGUgYXNzaWdubWVudCB1c2luZyBgPC1gIGFuZCBgPWAuIE5vdGUgdGhhdCBSIGlzIGNhc2Ugc2Vuc2l0aXZlIGZvciBldmVyeXRoaW5nCgpgYGB7cn0KcGF5IDwtIDI1MAoKbW9udGggPSAxMgoKcGF5ICogbW9udGgKCnNhbGFyeSA8LSBwYXkgKiBtb250aApgYGAKCgpGZXcgcG9pbnRzIGluIG5hbWluZyB2YXJpYWJsZXMgYW5kIHZlY3RvcnM6IHVzZSBzaG9ydCwgaW5mb3JtYXRpdmUgd29yZHMsIGtlZXAgc2FtZSBtZXRob2QgKGUuZy4sIHlvdSBjYW4gdXNlIGNhcGl0YWwgbGV0dGVycyBidXQgaXQgaXMgbm90IHJlY29tbWVuZGVkLCB1c2Ugb25seSBfIG9yIC4gKS4KCiMjIEZ1bmN0aW9uIApGdW5jdGlvbiBpcyBhIHNldCBvZiBzdGF0ZW1lbnRzIGNvbWJpbmVkIHRvZ2V0aGVyIHRvIHBlcmZvcm0gYSBzcGVjaWZpYyB0YXNrLiBXaGVuIHdlIHVzZSBhIGJsb2NrIG9mIGNvZGUgcmVwZWF0ZWRseSwgd2UgY2FuIGNvbnZlcnQgaXQgdG8gYSBmdW5jdGlvbi4gVG8gd3JpdGUgYSBmdW5jdGlvbiwgZmlyc3QsIHlvdSBuZWVkIHRvICpkZWZpbmUqIGl0OgoKYGBge3J9Cm15X211bHRpcGxpZXIgPC0gZnVuY3Rpb24oYSxiKXsKICByZXN1bHQgPSBhICogYgogIHJldHVybiAocmVzdWx0KQp9CmBgYAoKVGhpcyBjb2RlIGRvIG5vdGhpbmcuIFRvIGdldCBhIHJlc3VsdCwgeW91IG5lZWQgdG8gKmNhbGwqIGl0OgoKYGBge3J9Cm15X211bHRpcGxpZXIgKGE9MiwgYj00KQojIG9yOiBteV9tdWx0aXBsaWVyICgyLCA0KQpgYGAKCldlIGNhbiBzZXQgYSBkZWZhdWx0IHZhbHVlIGZvciBvdXIgYXJndW1lbnRzOgoKYGBge3J9Cm15X211bHRpcGxpZXIyIDwtIGZ1bmN0aW9uKGEsYj00KXsKICByZXN1bHQgPSBhICogYgogIHJldHVybiAocmVzdWx0KQp9CgpteV9tdWx0aXBsaWVyMiAoYT0yKQojIG9yOiBteV9tdWx0aXBsaWVyICgyKQojIG9yOiBteV9tdWx0aXBsaWVyICgyLCA2KQpgYGAKCkZvcnR1bmF0ZWx5LCB5b3UgZG8gbm90IG5lZWQgdG8gd3JpdGUgZXZlcnl0aGluZyBmcm9tIHNjcmF0Y2guIFIgaGFzIGxvdHMgb2YgYnVpbHQtaW4gZnVuY3Rpb25zIHRoYXQgeW91IGNhbiB1c2U6CmBgYHtyfQpyb3VuZCg1NC42Nzg3KQpyb3VuZCg1NC41Nzg3LCBkaWdpdHMgPSAyKQpgYGAKClVzZSBgP2AgYmVmb3JlIHRoZSBmdW5jdGlvbiBuYW1lIHRvIGdldCBzb21lIGhlbHAuIEZvciBleGFtcGxlLCBgP3JvdW5kYC4gWW91IHdpbGwgc2VlIG1hbnkgZnVuY3Rpb25zIGluIHRoZSByZXN0IG9mIHRoZSB3b3Jrc2hvcC4KCiMjIERhdGEgVHlwZXMKCmZ1bmN0aW9uIGBjbGFzcygpYCBpcyB1c2VkIHRvIHNob3cgd2hhdCBpcyB0aGUgdHlwZSBvZiBhIHZhcmlhYmxlLgoKCjEuICpMb2dpY2FsKjogYFRSVUVgLCBgRkFMU0VgIGNhbiBiZSBhYmJyZXZpYXRlZCBhcyBgVGAsIGBGYC4gIFRoZXkgaGFzIHRvIGJlIGNhcGl0YWwsICd0cnVlJyBpcyBub3QgYSBsb2dpY2FsIGRhdGE6CmBgYHtyfQpjbGFzcyhUUlVFKQpjbGFzcyhGKQpgYGAKCjIuICpOdW1lcmljKjogYWxsIG51bWJlcnMgZS5nLiA1LCAgMTAuNSwgIDExLDM3OyAgYSBzcGVjaWFsIHR5cGUgb2YgbnVtZXJpYyBpcyAiaW50ZWdlciIgd2hpY2ggaXMgbnVtYmVycyB3aXRob3V0IGRlY2ltYWwuIEludGVnZXJzIGFyZSBhbHdheXMgbnVtZXJpYywgYnV0IG51bWVyaWMgaXMgbm90IGFsd2F5cyBpbnRlZ2VyOgpgYGB7cn0KY2xhc3MoMikKY2xhc3MoMTMuNDYpCmBgYAoKMy4gKkNoYXJhY3Rlcio6IHRleHQgZm9yIGV4YW1wbGUsICJJIGxvdmUgUiIgb3IgIjQiIG9yICI0LjUiOgpgYGB7cn0KY2xhc3MoImhhIGhhIGhhIGhhIikKY2xhc3MoIjU2LjYiKQpjbGFzcygiVFJVRSIpCmBgYAoKQ2FuIHdlIGNoYW5nZSB0aGUgdHlwZSBvZiBkYXRhIGluIGEgdmFyaWFibGU/IFllcywgeW91IG5lZWQgdG8gdXNlIHRoZSBmdW5jdGlvbiBgYXMuLS0tKClgCgpgYGB7cn0KYXMubnVtZXJpYyhUUlVFKQphcy5jaGFyYWN0ZXIoNCkKYXMubnVtZXJpYygiNC41IikKYXMubnVtZXJpYygiSGVsbG8iKQpgYGAKCgojIyBEYXRhIFN0cnVjdHVyZXMKCgojIyMgVmVjdG9yIAoKV2hlbiB0aGVyZSBhcmUgbW9yZSB0aGFuIG9uZSBudW1iZXIgb3IgbGV0dGVyIHN0b3JlZC4gVXNlIHRoZSBjb21iaW5lIGZ1bmN0aW9uIGMoKSBmb3IgdGhhdC4KCmBgYHtyfQpzYWxlIDwtIGMoMSwgMiwgMyw0LCA1LCA2LCA3LCA4LCA5LCAxMCkgIyBhbHNvIHNhbGUgPC0gYygxOjEwKQoKc2FsZSA8LSBjKDE6MTApCgpzYWxlICogc2FsZQpgYGAKCipTdWJzZXR0aW5nIGEgdmVjdG9yKjoKCmBgYHtyfQpkYXlzIDwtIGMoIlNhdHVyZGF5IiwgIlN1bmRheSIsICJNb25kYXkiLCAiVHVlc2RheSIsICJXZWRuZXNkYXkiLCAiVGh1cnNkYXkiLCAiRnJpZGF5IikKCmRheXNbMl0KZGF5c1stMl0KCmRheXNbYygyLCAzLCA0KV0KYGBgCgoKKiAqRXhlcmNpc2UqOiBDcmVhdGUgYSB2ZWN0b3IgbmFtZWQgYG15X3ZlY3RvcmAgd2l0aCBudW1iZXJzIGZyb20gMCB0byAxMDAwIGluIGl0IGFuZCBjYWxjdWxhdGUgbWVhbiwgbWVkaWFuLCBzZCwgbWluLCBtYXgsIGFuZCBzdW0gb2YgdGhhdCB2ZWN0b3I6CgpgYGB7cn0KbXlfdmVjdG9yIDwtICgwOjEwMDApCgptZWFuKG15X3ZlY3RvcikKbWVkaWFuKG15X3ZlY3RvcikKbWluKG15X3ZlY3RvcikKcmFuZ2UobXlfdmVjdG9yKQpjbGFzcyhteV92ZWN0b3IpCnN1bShteV92ZWN0b3IpCnNkKG15X3ZlY3RvcikKYGBgCgojIyMgTGlzdAoKTGlzdCBhbGxvd3MgeW91IHRvIGdhdGhlciBhIHZhcmlldHkgb2Ygb2JqZWN0cyB1bmRlciBvbmUgbmFtZSAodGhhdCBpcywgdGhlIG5hbWUgb2YgdGhlIGxpc3QpIGluIGFuIG9yZGVyZWQgd2F5LiBUaGVzZSBvYmplY3RzIGNhbiBiZSBtYXRyaWNlcywgdmVjdG9ycywgZGF0YSBmcmFtZXMsIGV2ZW4gb3RoZXIgbGlzdC4KCmBgYHtyfQpteV9saXN0ID0gbGlzdChzYWxlLCAxLCAzLCA0OjcsICJIRUxMTyIsICJoZWxsbyIsIEZBTFNFKQpteV9saXN0CmBgYAoKIyMjIEZhY3RvcgpGYWN0b3JzIHN0b3JlIHRoZSB2ZWN0b3IgYWxvbmcgd2l0aCB0aGUgZGlzdGluY3QgdmFsdWVzIG9mIHRoZSBlbGVtZW50cyBpbiB0aGUgdmVjdG9yIGFzIGxhYmVscy4gVGhlIGxhYmVscyBhcmUgYWx3YXlzIGNoYXJhY3RlciBpcnJlc3BlY3RpdmUgb2Ygd2hldGhlciBpdCBpcyBudW1lcmljIG9yIGNoYXJhY3Rlci4gRm9yIGV4YW1wbGUsIHZhcmlhYmxlIGdlbmRlciB3aXRoICJtYWxlIiBhbmQgImZlbWFsZSIgZW50cmllczoKCmBgYHtyfQpnZW5kZXIgPC0gYygibWFsZSIsICJtYWxlIiwgIm1hbGUiLCAiIGZlbWFsZSIsICJmZW1hbGUiLCAiZmVtYWxlIikKZ2VuZGVyIDwtIGZhY3RvcihnZW5kZXIpCmBgYAoKUiBub3cgdHJlYXRzIGdlbmRlciBhcyBhIG5vbWluYWwgKGNhdGVnb3JpY2FsKSB2YXJpYWJsZTogMT1mZW1hbGUsIDI9bWFsZSBpbnRlcm5hbGx5IChhbHBoYWJldGljYWxseSkuCmBgYHtyfQpzdW1tYXJ5KGdlbmRlcikKYGBgCgoqICpRdWVzdGlvbio6IHdoeSB3aGVuIHdlIHJhbiB0aGUgYWJvdmUgZnVuY3Rpb24gaS5lLiBzdW1tYXJ5KCksIGl0IHNob3dlZCB0aHJlZSBhbmQgbm90IHR3byBsZXZlbHMgb2YgdGhlIGRhdGE/ICpIaW50KjogcnVuICdnZW5kZXInLgoKYGBge3J9CmdlbmRlcgpgYGAKClNvLCBiZSBjYXJlZnVsIG9mIHNwYWNlcyEKCiogKkV4ZXJjaXNlKjogQ3JlYXRlIGEgZ2VuZGVyIGZhY3RvciB3aXRoIDMwIG1hbGUgYW5kIDQwIGZlbWFsZXMgKCpIaW50KjogdXNlIHRoZSBgcmVwKClgIGZ1bmN0aW9uKToKYGBge3J9CmdlbmRlciA8LSBjKHJlcCgibWFsZSIsMzApLCByZXAoImZlbWFsZSIsIDQwKSkKZ2VuZGVyIDwtIGZhY3RvcihnZW5kZXIpCmdlbmRlcgpgYGAKClRoZXJlIGFyZSB0d28gdHlwZXMgb2YgY2F0ZWdvcmljYWwgdmFyaWFibGVzOiBub21pbmFsIGFuZCBvcmRpbmFsLiBIb3cgdG8gY3JlYXRlIG9yZGVyZWQgZmFjdG9ycyAod2hlbiB0aGUgdmFyaWFibGUgaXMgbm9taW5hbCBhbmQgdmFsdWVzIGNhbiBiZSBvcmRlcmVkKT8gV2Ugc2hvdWxkIGFkZCB0d28gYWRkaXRpb25hbCBhcmd1bWVudHMgdG8gdGhlIGBmYWN0b3IoKWAgZnVuY3Rpb246IGBvcmRlcmVkID0gVFJVRWAsIGFuZCBgbGV2ZWxzID0gYygibGV2ZWwxIiwgImxldmVsMiIpYC4gRm9yIGV4YW1wbGUsIHdlIGhhdmUgYSB2ZWN0b3IgdGhhdCBzaG93cyBwYXJ0aWNpcGFudHMnIGVkdWNhdGlvbiBsZXZlbC4KCmBgYHtyfQplZHU8LWMoMywyLDMsNCwxLDIsMiwzLDQpCgplZHVjYXRpb248LWZhY3RvcihlZHUsIG9yZGVyZWQgPSBUUlVFKQpsZXZlbHMoZWR1Y2F0aW9uKSA8LSBjKCJQcmltYXJ5IHNjaG9vbCIsImhpZ2ggc2Nob29sIiwiQ29sbGVnZSIsIlVuaSBncmFkdWF0ZWQiKQplZHVjYXRpb24KYGBgCgoqICpFeGVyY2lzZSo6IFdlIGhhdmUgYSBmYWN0b3Igd2l0aCBgcGF0aWVudGAgYW5kIGBjb250cm9sYCB2YWx1ZXMuIEhlcmUsIHRoZSBmaXJzdCBsZXZlbCBpcyBjb250cm9sIGFuZCB0aGUgc2Vjb25kIGxldmVsIGlzIHBhdGllbnQuIENoYW5nZSB0aGUgb3JkZXIgb2YgbGV2ZWxzLCBzbyBwYXRpZW50IHdvdWxkIGJlIHRoZSBmaXJzdCBsZXZlbDoKCmBgYHtyfQpoZWFsdGhfc3RhdHVzIDwtIGZhY3RvcihjKHJlcCgncGF0aWVudCcsNSkscmVwKCdjb250cm9sJyw1KSkpCmhlYWx0aF9zdGF0dXMKCmhlYWx0aF9zdGF0dXNfcmVvcmRlcmVkIDwtIGZhY3RvcihoZWFsdGhfc3RhdHVzLCBsZXZlbHMgPSBjKCdwYXRpZW50JywnY29udHJvbCcpKQpoZWFsdGhfc3RhdHVzX3Jlb3JkZXJlZApgYGAKCkZpbmFsbHksIGNhbiB5b3UgcmVsYWJlbCBib3RoIGxldmVscyB0byB1cHBlcmNhc2UgY2hhcmFjdGVycz8gKCpIaW50KjogY2hlY2sgYD9mYWN0b3JgKQoKYGBge3J9CmhlYWx0aF9zdGF0dXNfcmVsYWJlbGVkIDwtIGZhY3RvcihoZWFsdGhfc3RhdHVzLCBsZXZlbHMgPSBjKCdwYXRpZW50JywnY29udHJvbCcpLCBsYWJlbHMgPSBjKCdQYXRpZW50JywnQ29udHJvbCcpKQpoZWFsdGhfc3RhdHVzX3JlbGFiZWxlZApgYGAKCgojIyMgTWF0cmljZXMKQWxsIGNvbHVtbnMgaW4gYSBtYXRyaXggbXVzdCBoYXZlIHRoZSBzYW1lIG1vZGUobnVtZXJpYywgY2hhcmFjdGVyLCBldGMuKSBhbmQgdGhlIHNhbWUgbGVuZ3RoLiBJdCBjYW4gYmUgY3JlYXRlZCB1c2luZyBhIHZlY3RvciBpbnB1dCB0byB0aGUgbWF0cml4IGZ1bmN0aW9uLgoKYGBge3J9Cm15X21hdHJpeCA9IG1hdHJpeChjKDEsMiwzLDQsNSw2LDcsOCw5KSwgbnJvdyA9IDMsIG5jb2wgPSAzKQoKbXlfbWF0cml4CmBgYAoKIyMjIERhdGEgZnJhbWVzIAoKRGF0YSBmcmFtZXMgY2FuIGhvbGQgbnVtZXJpYywgY2hhcmFjdGVyIG9yIGxvZ2ljYWwgdmFsdWVzLiBXaXRoaW4gYSBjb2x1bW4gYWxsIGVsZW1lbnRzIGhhdmUgdGhlIHNhbWUgZGF0YSB0eXBlLCBidXQgZGlmZmVyZW50IGNvbHVtbnMgY2FuIGJlIG9mIGRpZmZlcmVudCBkYXRhIHR5cGUuIExldCdzIGNyZWF0ZSBhIGRhdGFmcmFtZToKCmBgYHtyfQppZCA8LSAxOjIwMApncm91cCA8LSBjKHJlcCgiUHN5Y2hvdGhlcmFweSIsIDEwMCksIHJlcCgiTWVkaWNhdGlvbiIsIDEwMCkpCnJlc3BvbnNlIDwtIGMocm5vcm0oMTAwLCBtZWFuID0gMzAsIHNkID0gNSksCiAgICAgICAgICAgICBybm9ybSgxMDAsIG1lYW4gPSAyNSwgc2QgPSA1KSkKCm15X2RhdGFmcmFtZSA8LWRhdGEuZnJhbWUoUGF0aWVudCA9IGlkLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRyZWF0bWVudCA9IGdyb3VwLAogICAgICAgICAgICAgICAgICAgICAgICAgIFJlc3BvbnNlID0gcmVzcG9uc2UpCmBgYAoKV2UgYWxzbyBjb3VsZCBoYXZlIGRvbmUgdGhlIGJlbG93CgpgYGB7cn0KbXlfZGF0YWZyYW1lIDwtZGF0YS5mcmFtZShQYXRpZW50ID0gYygxOjIwMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgVHJlYXRtZW50ID0gYyhyZXAoIlBzeWNob3RoZXJhcHkiLCAxMDApLCByZXAoIk1lZGljYXRpb24iLCAxMDApKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBSZXNwb25zZSA9IGMocm5vcm0oMTAwLCBtZWFuID0gMzAsIHNkID0gNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJub3JtKDEwMCwgbWVhbiA9IDI1LCBzZCA9IDUpKSkKYGBgCgpJbiBsYXJnZSBkYXRhIHNldHMsIHRoZSBmdW5jdGlvbiBoZWFkKCkgZW5hYmxlcyB5b3UgdG8gc2hvdyB0aGUgZmlyc3Qgb2JzZXJ2YXRpb25zIG9mIGEgZGF0YSBmcmFtZXMuIFNpbWlsYXJseSwgdGhlIGZ1bmN0aW9uIHRhaWwoKSBwcmludHMgb3V0IHRoZSBsYXN0IG9ic2VydmF0aW9ucyBpbiB5b3VyIGRhdGEgc2V0LgoKYGBge3IgZXZhbD1GfQpoZWFkKG15X2RhdGFmcmFtZSkgCnRhaWwobXlfZGF0YWZyYW1lKQpgYGAKCmBgYHtyIGVjaG89Rn0KaGVhZChteV9kYXRhZnJhbWUpICU+JQogIG11dGF0ZShgIGA9IGMoMTo2KSkgJT4lCiAgc2VsZWN0KGAgYCwgUGF0aWVudCwgVHJlYXRtZW50LAlSZXNwb25zZSkgJT4lCiAga25pdHI6OmthYmxlKCkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCgp0YWlsKG15X2RhdGFmcmFtZSklPiUKICBrbml0cjo6a2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gVCkKYGBgCgpTaW1pbGFyIHRvIHZlY3RvcnMgYW5kIG1hdHJpY2VzLCBicmFja2V0cyBbXSBhcmUgdXNlZCB0byBzZWxlY3RzIGRhdGEgZnJvbSByb3dzIGFuZCBjb2x1bW5zIGluIGRhdGEuZnJhbWVzOgoKYGBge3J9Cm15X2RhdGFmcmFtZVszNSwgM10KYGBgCgoqICpFeGVyY2lzZSo6IEhvdyBjYW4gd2UgZ2V0IGFsbCBjb2x1bW5zLCBidXQgb25seSBmb3IgdGhlIGZpcnN0IDEwIHBhcnRpY2lwYW50cz8KCmBgYHtyIGV2YWw9Rn0KbXlfZGF0YWZyYW1lWzE6MTAsIF0KYGBgCgpgYGB7ciBlY2hvPUZ9CmtuaXRyOjprYWJsZShteV9kYXRhZnJhbWVbMToxMCwgXSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCgpgYGAKSG93IHRvIGdldCBvbmx5IHRoZSBSZXNwb25zZSBjb2x1bW4gZm9yIGFsbCBwYXJ0aWNpcGFudHM/CgpgYGB7cn0KbXlfZGF0YWZyYW1lWyAsIDNdCmBgYAoKQW5vdGhlciBlYXNpZXIgd2F5IGZvciBzZWxlY3RpbmcgcGFydGljdWxhciBpdGVtcyBpcyB1c2luZyB0aGVpciBuYW1lcyB0aGF0IGlzIG1vcmUgaGVscGZ1bCB0aGFuIG51bWJlciBvZiB0aGUgcm93cyBpbiBsYXJnZSBkYXRhIHNldHM6CmBgYHtyIGV2YWw9Rn0KbXlfZGF0YWZyYW1lWyAsICJSZXNwb25zZSJdCiMgT1I6Cm15X2RhdGFmcmFtZSRSZXNwb25zZQoKYGBgCgpTbyBmYXIsIHdlIGNyZWF0ZWQgZGF0YWZyYW1lcyB1c2luZyBgZGF0YS5mcmFtZWAgZnVuY3Rpb24gZnJvbSB0aGUgYmFzZSBSLiBIb3dldmVyLCBhIGJldHRlciB3YXkgdG8gY3JlYXRlIGRhdGFmcmFtZXMgaXMgdG8gdXNlIHRoZSBgdGliYmxlYCBmdW5jdGlvbiBmcm9tIHRpZHl2ZXJzZSAoc2VlIFtoZXJlXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3RpYmJsZXMuaHRtbCkpLgoKIyBEYXRhIENsZWFuaW5nCgpgYGB7ciBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjcwMHB4Iiwgb3V0LmhlaWdodD0iMzUwcHgiLCBmaWcuY2FwPSAiQXJ0d29yayBieSBBbGxpc29uIEhvcnN0OiBodHRwczovL2dpdGh1Yi5jb20vYWxsaXNvbmhvcnN0L3N0YXRzLWlsbHVzdHJhdGlvbnMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCdlbnZpcm9ubWVudGFsLWRhdGEtc2NpZW5jZS1yNGRzLWdlbmVyYWwucG5nJykpCmBgYAoKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJBcnR3b3JrIGJ5IEFsbGlzb24gSG9yc3Q6IGh0dHBzOi8vZ2l0aHViLmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucyJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ2NyYWNrZWRfc2V0d2QucG5nJykpCmBgYAoKTm93LCBzdXBwb3NlIHdlIHJhbiBhbiBleHBlcmltZW50IHdpdGggMTQxIGRlcHJlc3NlZCBwYXRpZW50cy4gUGFydGljaXBhbnRzIHdlcmUgcmFuZG9tbHkgYXNzaWduZWQgaW50byB0d28gdHJlYXRtZW50IGdyb3VwczogQ0JUIG9yIFBzeWNob2R5bmFtaWMgcHN5Y2hvdGhlcmFweS4gV2UgbWVhc3VyZWQgc2VsZi1yZXBvcnQgZGVwcmVzc2lvbiBzY29yZXMgYXQgNSBkaWZmZXJlbnQgc3RhZ2VzIG9mIHRyZWF0bWVudDogCgotIFN0YWdlIDE6IEJlZm9yZSBzdGFydGluZyBhbnkgdHJlYXRtZW50LiBJdCBpcyBvdXIgYmFzZSBzdGFnZSAocHJlLXRlc3QpCi0gU3RhZ2UgMjogQWZ0ZXIgNSBzZXNzaW9ucyBvZiBwc3ljaG90aGVyYXB5IChwb3N0LXRlc3QxKQotIFN0YWdlIDM6IEFmdGVyIDEwIHNlc3Npb25zIG9mIHBzeWNob3RoZXJhcHkgKHBvc3QtdGVzdDIpCi0gU3RhZ2UgNDogQXQgdGhlIGVuZCBvZiB0aGUgdHJlYXRtZW50IChwb3N0LXRlc3QzKQotIFN0YWdlIDU6IFRocmVlIG1vbnRocyBhZnRlciB0aGUgdHJlYXRtZW50IChwb3N0LXRlc3Q0KQoKbGV0J3MgcmVhZCBhbmQgY2hlY2sgdGhlIHVuY2xlYW5lZCBkYXRhLiBCdXQsIGZpcnN0IHRoaW5nIGZpcnN0LiBsZXQncyBpbnN0YWxsIGFuZCB0aGVuIGxvYWQgdGhlIHRpZHl2ZXNlIHBhY2thZ2UuIFdlIGFsc28gbmVlZCBzb21lIG90aGVyIHBhY2thZ2VzOgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGV2YWw9Rn0KCiMgSW5zdGFsbCBpdAppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQoKIyBBbmQgdGhlbiBsb2FkIGl0CmxpYnJhcnkodGlkeXZlcnNlKQoKIyBMb2FkIG90aGVyIHBhY2thZ2VzIHRoYXQgeW91IGhhdmUgYWxyZWFkeSBpbnN0YWxsZWQKbGlicmFyeShoZXJlKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkoYnJvb20pCmxpYnJhcnkoYWZleCkKbGlicmFyeShlbW1lYW5zKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoZ2dzY2kpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KHNraW1yKQojIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImVhc3lzdGF0cy9jb3JyZWxhdGlvbiIpCmxpYnJhcnkoImNvcnJlbGF0aW9uIikKb3B0aW9ucyhzY2lwZW49OTk5KSAjIHR1cm4gb2ZmIHNjaWVudGlmaWMgbm90YXRpb25zCm9wdGlvbnMoY29udHJhc3RzID0gYygnY29udHIuc3VtJywnY29udHIucG9seScpKSAjIHNldCB0aGUgY29udHJhc3Qgc3VtIGdsb2JhbGx5IApvcHRpb25zKGtuaXRyLmthYmxlLk5BID0gJycpCmBgYAoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGV2YWw9Rn0KIyByZWFkIHRoZSByYXcgZGF0YQpyYXdfZGF0YSA8LSByZWFkX2NzdihoZXJlKCJyYXdfZGF0YSIsInJhd19kYXRhX2V4cDEuY3N2IikpCmhlYWQocmF3X2RhdGEpCmBgYAoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGVjaG89Rn0KIyByZWFkIHRoZSByYXcgZGF0YQpyYXdfZGF0YSA8LSByZWFkX2NzdihoZXJlKCJyYXdfZGF0YSIsInJhd19kYXRhX2V4cDEuY3N2IikpCgprbml0cjo6a2FibGUoaGVhZChyYXdfZGF0YSkpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBGKSU+JQogIHNjcm9sbF9ib3god2lkdGggPSAiNzgwcHgiKQpgYGAKCiogKkV4ZXJjaXNlKjogVGhlcmUgaXMgYSBkYXRhc2V0IGluIHRoZSBgY2xlYW5lZF9kYXRhYCBmb2xkZXIgbmFtZWQgYHVuaWNlZl91NW1yLmNzdmAuIFJlYWQgdGhlIGRhdGFzZXQgdXNpbmcgYHJlYWRfY3N2YCBhbmQgYGhlcmVgLgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KdW5pY2VmX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwidW5pY2VmX3U1bXIuY3N2IikpCmBgYAoKYGBge3IgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI3MDBweCIsIG91dC5oZWlnaHQ9IjM1MHB4IiwgZmlnLmNhcD0gIkFydHdvcmsgYnkgQWxsaXNvbiBIb3JzdDogaHR0cHM6Ly9naXRodWIuY29tL2FsbGlzb25ob3JzdC9zdGF0cy1pbGx1c3RyYXRpb25zIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgnaW5wdXRzJywndGlkeWRhdGFfMy5qcGcnKSkKYGBgCgpJbiBvcmRlciB0byBjbGVhbiB0aGUgZGF0YSwgd2UgdXNlICp0aWR5dmVyc2UqIHdoaWNoIGlzIGEgY29sbGVjdGlvbiBvZiBwYWNrYWdlcyB0byB3b3JrIHdpdGggZGF0YS4gT25lIG9mIHRoZSB0aWR5dmVyc2UgcGFja2FnZXMgdGhhdCB3ZSB1c2UgcmVndWxhcmx5IGlzIGBkcGx5cmAgd2hpY2ggaW5jbHVkZXMgc2V2ZXJhbCBmdW5jdGlvbnM6CgotIGBtdXRhdGUoKWAgYWRkcyBuZXcgdmFyaWFibGVzIG9yIGNoYW5nZSBleGlzdGluZyBvbmVzLgotIGBzZWxlY3QoKWAgcGljayB2YXJpYWJsZXMgKGNvbHVtbnMpIGJhc2VkIG9uIHRoZWlyIG5hbWVzLgotIGBmaWx0ZXIoKWAgcGlja3MgY2FzZXMgKHJvd3MpIGJhc2VkIG9uIHRoZWlyIHZhbHVlcy4KLSBgc3VtbWFyaXNlKClgIGdpdmVzIGEgc2luZ2xlIHNpbmdsZSBzdW1tYXJ5IG9mIHRoZSBkYXRhIChlLmcuLCBtZWFuLCBjb3VudHMsIGV0Yy4pCi0gYGFycmFuZ2UoKWAgY2hhbmdlcyB0aGUgb3JkZXJpbmcgb2YgdGhlIHJvd3MuCi0gYGdyb3VwX2J5KClgIGRpdmlkZXMgeW91ciBkYXRhZnJhbWUgaW50byBncm91cGVkIGRhdGFmcmFtZXMgYW5kIGFsbG93IHlvdSB0byBkbyBlYWNoIG9mIHRoZSBhYm92ZSBvcGVyYXRpb25zIChleGNlcHQgZm9yIGBhcnJhbmdlYCkgb24gZXZlcnkgb25lIG9mIHRoZW0gc2VwYXJhdGVseS4KCiMjIFNlbGVjdAoKUGljayBgc3ViamVjdGAsIGBhZ2VgLCBhbmQgYGdlbmRlcmAgY29sdW1uczoKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBldmFsPUZ9CnNlbGVjdGVkX2RhdGEgPC0gc2VsZWN0KHJhd19kYXRhLCBzdWJqZWN0LCBhZ2UsIGdlbmRlcikKYGBgCgojIyBGaWx0ZXIKTm93LCBkbyB0aGUgZm9sbG93aW5nIHRhc2tzOiBwaWNrIGFsbCB0aGUgbWFsZSBwYXJ0aWNpcGFudHMsIHBpY2sgYWxsIHRoZSBtYWxlIHBhcnRpY2lwYW50cyAqKm9yKiogdGhvc2UgZ3JlYXRlciB0aGFuIDI1IHllYXJzIG9sZCwgYW5kIGZpbmFsbHkgcGljayBhbGwgbWFsZSBwYXJ0aWNpcGFudHMgKiphbmQqKiB0aG9zZSBncmVhdGVyIHRoYW4gMjUgeWVhcnMgb2xkOgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGV2YWw9Rn0KIyBmaWx0ZXIgYWxsIG1hbGVzCmZpbF9tYWxlIDwtIGZpbHRlcihyYXdfZGF0YSwgZ2VuZGVyID09ICJNYWxlIikKIyBmaWx0ZXIgbWFsZXMgYW5kIG9sZGVyIHRoYW4gMjUKZmlsX21hbGVfYW5kX2cyNSA8LSBmaWx0ZXIocmF3X2RhdGEsIGdlbmRlciA9PSAiTWFsZSIgJiBhZ2UgPiAyNSApCiMgZmlsdGVyIG1hbGVzIG9yIG9sZGVyIHRoYW4gMjUKZmlsX21hbGVfb3JfZzI1IDwtIGZpbHRlcihyYXdfZGF0YSwgZ2VuZGVyID09ICJNYWxlIiB8IGFnZSA+IDI1ICkKYGBgCgojIyBBcnJhbmdlIApBcnJhbmdlIChvcmRlcikgeW91ciBkYXRhZnJhbWUgYmFzZWQgb24gdGhlIGFnZSwgb25jZSBpbiBhbiBhc2NlbmRpbmcgb3JkZXIgKHlvdW5nZXJzIGZpcnN0KSBhbmQgb25jZSBiYXNlZCBvbiBkZXNjZW5kaW5nIG9yZGVyIChvbGRlcnMgZmlyc3QpOgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CiMgb3JkZXIgcGFydGljaXBhbnRzIGJhc2VkIG9uIHRoZWlyIGFnZQphcnJhbmdlZF9kYXRhIDwtIGFycmFuZ2UocmF3X2RhdGEsIGFnZSkKIyBvcmRlciBwYXJ0aWNpcGFudHMgYmFzZWQgb24gdGhlaXIgYWdlIChkZXNjZW5kZWluZykKYXJyYW5nZWRfZGVzY2VuZGluZyA8LSBhcnJhbmdlKHJhd19kYXRhLCBkZXNjKGFnZSkpCmBgYAoKIyMgTXV0YXRlCkNyZWF0ZSBhIGNvbHVtbiB0byBzaG93IGlmIHRoZSBwYXJ0aWNpcGFudCBoYXMgZmluaXNoZWQgdGhlIHRhc2sgb3Igbm90OgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KbXV0YXRlZF9kYXRhIDwtIG11dGF0ZSAocmF3X2RhdGEsIGZpbmlzaGVkPSBjYXNlX3doZW4ocHJvZ3Jlc3M9PTEwMH4gIlllcyIsVH4gIk5vIikpCmBgYAoKIyMgU3VtbWFyaXNlClN1bW1hcml6ZSBwYXJ0aWNpcGFudHMgYWdlIGFuZCBzZDoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnN1bW1hcmlzZShyYXdfZGF0YSwgbWVhbj0gbWVhbihhZ2UsIG5hLnJtPVQpLAogICAgICAgICAgc2Q9IHNkIChhZ2UsIG5hLnJtPVQpKQpgYGAKCiMjIFBpcGUgT3BlcmF0b3JzCkEgbmV3IGZ1bmN0aW9uOiAqKnBpcGUgb3BlcmF0b3JzKiogYCU+JWAgcGlwZXMgYSB2YWx1ZSBpbnRvIHRoZSBuZXh0IGZ1bmN0aW9uOgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnJhd19kYXRhICU+JSAKICBzdW1tYXJpc2UoLiwgbWVhbj0gbWVhbihhZ2UsIG5hLnJtPVQpLAogICAgICAgICAgICBzZD0gc2QgKGFnZSwgbmEucm09VCkpCmBgYAoKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpyYXdfZGF0YSAlPiUgCiAgc3VtbWFyaXNlKG1lYW49IG1lYW4oYWdlLCBuYS5ybT1UKSwKICAgICAgICAgICAgc2Q9IHNkIChhZ2UsIG5hLnJtPVQpKQpgYGAKCkNhbGN1bGF0ZSB0aGUgYWdlIG1lYW4gb2YgeW91bmdlciB0aGFuIDI1IHBhcnRpY2lwYW50cyBvbmx5OgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnJhd19kYXRhICU+JSAKICBmaWx0ZXIgKGFnZSA8IDI1KSAlPiUKICBzdW1tYXJpc2UobWVhbj0gbWVhbihhZ2UsIG5hLnJtPVQpLAogICAgICAgICAgICBzZD0gc2QgKGFnZSwgbmEucm09VCkpCmBgYAoKIyMgR3JvdXAgQnkKCkNhbGN1bGF0ZSB0aGUgYWdlIG1lYW4gb2YgeW91bmdlciB0aGFuIDI1IHBhcnRpY2lwYW50cyAgZm9yIGVhY2ggZ2VuZGVyIHNlcGFyYXRlbHk6CgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcmF3X2RhdGEgJT4lIAogIGZpbHRlciAoYWdlIDwgMjUpICU+JQogIGdyb3VwX2J5KGdlbmRlcikgJT4lCiAgc3VtbWFyaXNlKG1lYW49IG1lYW4oYWdlLCBuYS5ybT1UKSwKICAgICAgICAgICAgc2Q9IHNkIChhZ2UsIG5hLnJtPVQpKSAlPiUKICB1bmdyb3VwICgpCmBgYCAgICAgICAgIAoKCiogKkV4ZXJjaXNlKjogQ3JlYXRlIGEgY29sdW1uIHRvIHNob3cgaWYgcGFydGljaXBhbnQgaXMgb2xkZXIgdGhhbiAyMyBvciBub3QgYW5kIHRoZW4gY2FsY3VsYXRlIHNsZWVwIHF1YWxpdHkgKGBzbGVlcF9xdWFsaXR5YCkgbWVhbiBmb3IgZWFjaCBncm91cCBzZXBhcmF0ZWx5OgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcmF3X2RhdGEgJT4lCiAgbXV0YXRlKGFnZV9ncm91cCA9IGNhc2Vfd2hlbihhZ2UgPiAyMyB+ICJncmVhdGVyIHRoYW4gMjMiLCBUfiAieW91bmdlciB0aGFuIDIzIikpICU+JQogIGdyb3VwX2J5KGFnZV9ncm91cCkgJT4lCiAgc3VtbWFyaXNlKHNsZWVwX3F1YWxpdHkgPSBtZWFuKHNsZWVwX3F1YWxpdHksIG5hLnJtPVQpKQpgYGAgICAgIAoKKiAqRXhlcmNpc2UqOiBBZGQgdGhlIGFueGlldHkgdG90YWwgc2NvcmUgKHN1bSkgdG8gdGhlIGRhdGFmcmFtZSBhbmQgdGhlbiBjb252ZXJ0IHN1YmplY3QgY29sdW1uIHRvIGZhY3RvcjoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmFueGlldHlfZGF0YSA8LSByYXdfZGF0YSAlPiUKICBtdXRhdGUoYW54aWV0eV90b3RhbD0gYW54aWV0eTErYW54aWV0eTIrYW54aWV0eTMrYW54aWV0eTQrYW54aWV0eTUrYW54aWV0eTYrYW54aWV0eTcrYW54aWV0eTgpICU+JQogIG11dGF0ZShzdWJqZWN0PSBmYWN0b3Ioc3ViamVjdCkpCmBgYCAKCiMjIFBpdm90aW5nCgpOZXh0LCB3ZSB3YW50IHRvIHBpdm90IG91ciBkYXRhIHRvIHN3aXRjaCBiZXR3ZWVuIGxvbmcgYW5kIHdpZGUgZm9ybWF0OgoKYGBge3IgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI3MDBweCIsIG91dC5oZWlnaHQ9IjM1MHB4IiwgZmlnLmNhcD0gIkFydHdvcmsgYnkgQWxsaXNvbiBIb3JzdDogaHR0cHM6Ly9naXRodWIuY29tL2FsbGlzb25ob3JzdC9zdGF0cy1pbGx1c3RyYXRpb25zIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgnaW5wdXRzJywndGlkeWRhdGFfMS5qcGcnKSkKYGBgCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KCiMgTWFrZSB5b3UgZGF0YSBsb25nCmxvbmdfZGF0YSA8LSByYXdfZGF0YSAlPiUKICBzZWxlY3Qoc3ViamVjdCwgc3RhZ2UxX2NidDpzdGFnZTVfY2J0LHN0YWdlMV9keW5hbWljOnN0YWdlNV9keW5hbWljKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGMoc3RhZ2UxX2NidDpzdGFnZTVfZHluYW1pYyksIG5hbWVzX3RvID0gJ3N0YWdlJywgdmFsdWVzX3RvID0gJ2RlcHJlc3Npb25fc2NvcmUnKQoKIyBNYWtlIHlvdSBkYXRhIHdpZGUKd2lkZV9kYXRhIDwtIGxvbmdfZGF0YSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gc3RhZ2UsIHZhbHVlc19mcm9tPSBkZXByZXNzaW9uX3Njb3JlKQoKYGBgCgoqICpFeGVyY2lzZSo6IENvbnZlcnQgdGhlIFVOSUNFRiBkYXRhc2V0IHRvIGxvbmcgYW5kIHdpZGUgZm9ybWF0czoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnVuaWNlZl9kYXRhIDwtIHJlYWRfY3N2KGhlcmUoImNsZWFuZWRfZGF0YSIsInVuaWNlZl91NW1yLmNzdiIpKQoKbGlicmFyeShqYW5pdG9yKQp1bmljZWZfZGF0YV9jbGVhbmVkIDwtIHVuaWNlZl9kYXRhICU+JQogIGNsZWFuX25hbWVzKCkKCnVuaWNlZl9sb25nX2RhdGEgPC0gdW5pY2VmX2RhdGFfY2xlYW5lZCAlPiUgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKHU1bXJfMTk1MDp1NW1yXzIwMTUpLCBuYW1lc190byA9ICd5ZWFyJywgdmFsdWVzX3RvID0gJ3U1bXInKQp1bmljZWZfd2lkZWdfZGF0YSA8LSB1bmljZWZfbG9uZ19kYXRhICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gJ3llYXInLCB2YWx1ZXNfZnJvbSA9ICd1NW1yJykKYGBgCgoqTm90ZSo6IFRoZSBjb2RlcyBmb3IgdGhlIHByZXZpb3VzIGV4ZXJjaXNlIHdlcmUgdGFrZW4gZnJvbSBbdGhpcyBibG9nIHBvc3RdKGh0dHBzOi8vc2VqZGVteXIuZ2l0aHViLmlvL3ItdHV0b3JpYWxzL2Jhc2ljcy93aWRlLWFuZC1sb25nLykgd3JpdHRlbiBieSBTaW1vbiBFamRlbXlyLgoKTm93LCBsZXQncyBkbyBzb21lIGNsZWFuaW5nIHVzaW5nIGBkcGx5cmAsIGB0aWR5cmAgYW5kIG90aGVyIGB0aWR5dmVyc2VgIGxpYnJhcmllczogCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBldmFsPUZ9CmNsZWFuZWRfZGF0YSA8LSByYXdfZGF0YSAlPiUgCiAgZmlsdGVyKHByb2dyZXNzID09IDEwMCkgJT4lICMgZmlsdGVyIG91dCB1bmZpbmlzaGVkIHBhcnRpY2lwYW50cwogIHNlbGVjdCgtY29uc2VudF9mb3JtKSAlPiUgI3JlbW92ZSBzb21lIHVzZWxlc3MgY29sdW1ucwogICMgY3JlYXRlIGEgdG90YWwgc2NvcmUgZm9yIG91ciBxdWVzdGlvbm5haXJlCiAgbXV0YXRlKGFueGlldHlfdG90YWw9IGFueGlldHkxK2FueGlldHkyK2FueGlldHkzK2FueGlldHk0K2FueGlldHk1K2FueGlldHk2K2FueGlldHk3K2FueGlldHk4KSAlPiUKICBzZWxlY3QoLWFueGlldHkxOi1hbnhpZXR5OCkgJT4lCiAgIyBtYWtlIG91ciBkYXRhZnJhbWUgbG9uZwogIHBpdm90X2xvbmdlcihjb2xzID0gYyhzdGFnZTFfY2J0OnN0YWdlNV9jYnQsc3RhZ2UxX2R5bmFtaWM6c3RhZ2U1X2R5bmFtaWMpLG5hbWVzX3RvID0gJ3N0YWdlJyx2YWx1ZXNfdG8gPSAnZGVwcmVzc2lvbl9zY29yZScpICU+JSAKICAjcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHN0YWdlLCB2YWx1ZXNfZnJvbT0gZGVwcmVzc2lvbl9zY29yZSkgIyB0aGlzIGNvZGUgY2hhbmdlIG91ciBkYXRhZnJhbWUgYmFjayB0byB3aWRlCiAgZmlsdGVyKCFpcy5uYShkZXByZXNzaW9uX3Njb3JlKSkgJT4lICNyZW1vdmUgcm93cyB3aXRoIGRlcHJlc3Npb25fc2NvcmUgPT0gTkEKICBtdXRhdGUoc3RhZ2U9IGdzdWIoIl8uKiIsICIiLCBzdGFnZSkpICU+JQogIHNlbGVjdCAoc3ViamVjdCwgYWdlLCBnZW5kZXIsIGdyb3VwLCBzdGFnZSwgZGVwcmVzc2lvbl9zY29yZSwgYW54aWV0eV90b3RhbCwgc2xlZXBfcXVhbGl0eSwgbGlmZV9zYXRpc2ZhY3Rpb24pCmBgYAoKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBlY2hvPUZ9CmNsZWFuZWRfZGF0YSA8LSByYXdfZGF0YSAlPiUgCiAgZmlsdGVyKHByb2dyZXNzID09IDEwMCkgJT4lICMgZmlsdGVyIG91dCB1bmZpbmlzaGVkIHBhcnRpY2lwYW50cwogIHNlbGVjdCgtY29uc2VudF9mb3JtKSAlPiUgI3JlbW92ZSBzb21lIHVzZWxlc3MgY29sdW1ucwogICMgY3JlYXRlIGEgdG90YWwgc2NvcmUgZm9yIG91ciBxdWVzdGlvbm5haXJlCiAgbXV0YXRlKGFueGlldHlfdG90YWw9IGFueGlldHkxK2FueGlldHkyK2FueGlldHkzK2FueGlldHk0K2FueGlldHk1K2FueGlldHk2K2FueGlldHk3K2FueGlldHk4KSAlPiUKICBzZWxlY3QoLWFueGlldHkxOi1hbnhpZXR5OCkgJT4lCiAgIyBtYWtlIG91ciBkYXRhZnJhbWUgbG9uZwogIHBpdm90X2xvbmdlcihjb2xzID0gYyhzdGFnZTFfY2J0OnN0YWdlNV9jYnQsc3RhZ2UxX2R5bmFtaWM6c3RhZ2U1X2R5bmFtaWMpLG5hbWVzX3RvID0gJ3N0YWdlJyx2YWx1ZXNfdG8gPSAnZGVwcmVzc2lvbl9zY29yZScpICU+JSAKICAjcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHN0YWdlLCB2YWx1ZXNfZnJvbT0gZGVwcmVzc2lvbl9zY29yZSkgIyB0aGlzIGNvZGUgY2hhbmdlIG91ciBkYXRhZnJhbWUgYmFjayB0byB3aWRlCiAgZmlsdGVyKCFpcy5uYShkZXByZXNzaW9uX3Njb3JlKSkgJT4lICNyZW1vdmUgcm93cyB3aXRoIGRlcHJlc3Npb25fc2NvcmUgPT0gTkEKICBtdXRhdGUoc3RhZ2U9IGdzdWIoIl8uKiIsICIiLCBzdGFnZSkpICU+JQogIHNlbGVjdCAoc3ViamVjdCwgYWdlLCBnZW5kZXIsIGdyb3VwLCBzdGFnZSwgZGVwcmVzc2lvbl9zY29yZSwgYW54aWV0eV90b3RhbCwgc2xlZXBfcXVhbGl0eSwgbGlmZV9zYXRpc2ZhY3Rpb24pCgprbml0cjo6a2FibGUoaGVhZChjbGVhbmVkX2RhdGEpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gRiklPiUKICBzY3JvbGxfYm94KHdpZHRoID0gIjc4MHB4IikKYGBgCgpPaywgbm93IHRoZSBkYXRhIGlzIGNsZWFuIGFuZCB0aWR5IHdoaWNoIG1lYW5zOgoKPiAxLiBFYWNoIHZhcmlhYmxlIGZvcm1zIGEgY29sdW1uLgoyLiBFYWNoIG9ic2VydmF0aW9uIGZvcm1zIGEgcm93LgozLiBFYWNoIHR5cGUgb2Ygb2JzZXJ2YXRpb25hbCB1bml0IGZvcm1zIGEgdGFibGUgKFtXaWNraGFtXShodHRwczovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy90aWR5LWRhdGEucGRmKSwgMjAxNCkuCgoKQ2hlY2sgdGhlIGRhdGFmcmFtZSBhbmQgYWxsIHRoZSBkYXRhIHR5cGVzOgpgYGB7cn0Kc3RyKGNsZWFuZWRfZGF0YSkKYGBgCgpGaW5hbGx5LCB3ZSBzYXZlIG91ciBkYXRhIHRvIHRoZSBgY2xlYW5lZF9kYXRhYCBmb2xkZXIuCgpgYGB7cn0Kd3JpdGVfY3N2KGNsZWFuZWRfZGF0YSwgaGVyZSgiY2xlYW5lZF9kYXRhIiwiY2xlYW5lZF9kYXRhX2V4cDEuY3N2IikpCmBgYAoKCiMgRGF0YSBWaXN1YWxpemF0aW9uCgpgYGB7ciBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjcwMHB4Iiwgb3V0LmhlaWdodD0iMzUwcHgiLCBmaWcuY2FwPSAiQXJ0d29yayBieSBBbGxpc29uIEhvcnN0OiBodHRwczovL2dpdGh1Yi5jb20vYWxsaXNvbmhvcnN0L3N0YXRzLWlsbHVzdHJhdGlvbnMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCdnZ3Bsb3QyX21hc3RlcnBpZWNlLnBuZycpKQpgYGAKCkJlZm9yZSBzdGFydGluZyB0aGUgZ2dwbG90LCBsZXQncyB0cnkgYSB2aXN1YWxpemF0aW9uIHVzaW5nIGEgZnVuY3Rpb24gZnJvbSB0aGUgQmFzZSBSIHRoZSBwbG90KCkgZnVuY3Rpb24gc2hvd3MgdGhlIGFzc29jaWF0aW9uIG9mIGVhY2ggdmFyaWFibGUgYWdhaW5zdCB0aGUgb3RoZXIgb25lIGluIGEgZGF0YSBoYW5keSBmb3IgZGF0YSB3aXRoIGZldyBudW1iZXIgb2YgdmFyaWFibGVzIHRvIHNlZSBpZiB0aGVyZSBhcmUgYW55IHBhdHRlcm5zCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTR9CgpleGFtX2RhdGE8LSByZWFkX2NzdihoZXJlOjpoZXJlKCJjbGVhbmVkX2RhdGEiLCAiZXhhbV9kYXRhLmNzdiIpKQoKcGxvdCh4ID0gZXhhbV9kYXRhJEFueGlldHksIHkgPSBleGFtX2RhdGEkRXhhbSkKCmBgYAoKVGhlIGNvZGUgYWxzbyB3b3JrcyB3aXRob3V0IHdyaXRpbmcgeCBhbmQgeSwgaG93ZXZlciwgd3JpdGluZyB0aGVtIGlzIHN0cm9uZ2x5IHJlY29tbWVuZGVkCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTR9CgpwbG90KGV4YW1fZGF0YSRBbnhpZXR5LCBleGFtX2RhdGEkRXhhbSkKYGBgCgpgZ2dwbG90YCwgdGhlIGdnIGluIGdncGxvdCBzdGFuZHMgZm9yIGdyYW1tYXIgb2YgZ3JhcGhpY3MuIEdyYW1tYXIgb2YgZ3JhcGhpY3MgYmFzaWNhbGx5IHNheXMgYW55IGdyYXBoaWNhbCByZXByZXNlbnRhdGlvbiBvZiBkYXRhLCBjYW4gYmUgcHJvZHVjZWQgYnkgYSBzZXJpZXMgb2YgbGF5ZXJzLiBZb3UgY2FuIHRoaW5rIG9mIGEgbGF5ZXIgYXMgYSBwbGFzdGljIHRyYW5zcGFyZW5jeS4gTGV0cyBkcmF3IHRoZSBzYW1lIHBsb3QgdXNpbmcgZ2dwbG90LiBBbHdheXMsIG1lbnRpb24gdGhlIGRhdGEgd2UgYXJlIGdvaW5nIHRvIHdvcmsgd2l0aC4KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD00fQpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBFeGFtLCB5ID0gQW54aWV0eSkpCmBgYAoKCi0gYGFlc2A6IGFlcyB3aGljaCBzdGFuZHMgZm9yIGFlc3RoZXRpY3MgaXMgYSByZWxhdGlvbnNoaXAgYmV0d2VlbiBhIHZhcmlhYmxlIGluIHlvdXIgZGF0YXNldCBhbmQgYW4gYXNwZWN0IG9mIHRoZSBwbG90IHRoYXQgaXMgZ29pbmcgdG8gdmlzdWFsbHkgY29udmV5IHRoZSBpbmZvcm1hdGlvbiB0byB0aGUgcmVhZGVyCgotIFZpc3VhbCBlbGVtZW50cyBhcmUga25vd24gYXMgZ2VvbXMgKHNob3J0IGZvciAnZ2VvbWV0cmljIG9iamVjdHMnKSBpbiBnZ3Bsb3QgMi4gV2hlbiB3ZSBkZWZpbmUgYSBsYXllciwgd2UgaGF2ZSB0byB0ZWxsIFIgd2hhdCBnZW9tIHdlIHdhbnQgZGlzcGxheWVkIG9uIHRoYXQgbGF5ZXIgKGRvIHdlIHdhbnQgYSBiYXIsIGxpbmUgZG90LCBldGMuPykKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gRXhhbSwgeSA9IEFueGlldHkpKSsgZ2VvbV9wb2ludCgpCmBgYAoKU28sIGxldHMgdHJ5IHNvbWUgb2YgdGhlbSBoZXJlIGxpa2Ugc2hhcGUgYW5kIHNpemUuIEJlIGNhcmVmdWwgd2l0aCB0aGUgKyBzaWduLCBpZiB5b3UgY2xpbmsgZW50ZXIgZm9yIHRoZSBuZXh0IHBhcnQgb2YgdGhlIGNvZGUsIHRoZSArIHNpZ24gc2hvdWxkIG5vdCBnbyB0byB0aGUgbmV4dCBsaW5lCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTR9CmdncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IEV4YW0sIHkgPSBBbnhpZXR5KSkrCiAgZ2VvbV9wb2ludChzaXplID0gMiwgc2hhcGUgPSA4KQpgYGAKClRoZSBjdXJyZW50IHBsb3QgaXMgbm90IHZlcnkgaW5mb3JtYXRpdmUgYWJvdXQgdGhlIHBhdHRlcm5zIGZvciBlYWNoIGdlbmRlci4KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD00fQpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBFeGFtLCB5ID0gQW54aWV0eSwgY29sb3IgPSBHZW5kZXIpKSsKICBnZW9tX3BvaW50KHNpemUgPSAyLCBzaGFwZSA9IDEwKQoKZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gRXhhbSwgeSA9IEFueGlldHksIGNvbG9yID0gR2VuZGVyLCBzaGFwZSA9IEdlbmRlcikpKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIHNoYXBlID0gMTApCmBgYAoKUXVlc3Rpb246IHdoeSB0aGUgYWJvdmUgY29kZSBkb2Vzbid0IG1ha2UgYW55IGNoYW5nZT8KCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gRXhhbSwgeSA9IEFueGlldHksIGNvbG9yID0gR2VuZGVyLCBzaGFwZSA9IEdlbmRlcikpKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIpCmBgYAoKQ2FuIGFzc2lnbiB0aGUgZmlyc3QgbGF5ZXIgdG8gYSB2YXJpYWJsZSB0byByZWR1Y2UgdGhlIGxlbmd0aCBvZiBjb2RlcyBmb3IgbmV4dCBsYXllcnMuCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTR9Ck15X2dyYXBoIDwtIGdncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IEV4YW0sIHkgPSBBbnhpZXR5KSkKCk15X2dyYXBoICsgZ2VvbV9wb2ludCgpCmBgYAoKbGV0cyBhZGQgYSBsaW5lIHRvIHRoZSBjdXJyZW50IGdyYXBoCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KTXlfZ3JhcGggKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCmBgYAoKQWVzdGhldGljcyBjYW4gYmUgc2V0IGZvciBhbGwgbGF5ZXJzIG9mIHRoZSBwbG90IChpLmUuLCBkZWZpbmVkIGluIHRoZSBwbG90IGFzIGEgd2hvbGUpIG9yIGNhbiBiZSBzZXQgaW5kaXZpZHVhbGx5IGZvciBlYWNoIGdlb20gaW4gYSBwbG90LgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD00fQpNeV9ncmFwaCArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gR2VuZGVyKSkgKyBnZW9tX3Ntb290aCgpCgpNeV9ncmFwaCArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gR2VuZGVyKSkgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBHZW5kZXIpKQpgYGAKClRoZSBzaGFkZWQgYXJlYSBhcm91bmQgdGhlIGxpbmUgaXMgdGhlIDk1JSBjb25maWRlbmNlIGludGVydmFsIGFyb3VuZCB0aGUgbGluZS4gV2UgY2FuIHN3aXRjaCB0aGlzIG9mZiBieSAgYWRkaW5nIGBzZSA9IEZgICh3aGljaCBpcyBzaG9ydCBmb3IgJ3N0YW5kYXJkIGVycm9yID0gRmFsc2UnKQoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD00fQpNeV9ncmFwaCArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKHNlID0gRikKYGBgCgoKV2hhdCBpZiB3ZSB3YW50IG91ciBsaW5lIHRvIGJlIGEgZGlyZWN0IGxpbmU/CmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KTXlfZ3JhcGggKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChzZSA9IEYsIG1ldGhvZCA9IGxtKQoKYGBgCkhvdyB0byBjaGFuZ2UgdGhlIGxhYmVscyBvZiB4IGFuZCB5IGF4ZXM/CmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KTXlfZ3JhcGggKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChzZSA9IEYsIG1ldGhvZCA9IGxtKSArCiAgbGFicyh4ID0gIkV4YW0gc2NvcmVzICUiLCB5ID0gIkFueGlldHkgc2NvcmVzIikKYGBgCgpIaXN0b2dyYW1zIGFyZSB1c2VkIHRvIHNob3cgZGlzdHJpYnV0aW9ucyBvZiB2YXJpYWJsZXMgd2hpbGUgYmFyIGNoYXJ0cyBhcmUgdXNlZCB0byBjb21wYXJlIHZhcmlhYmxlcy4gSGlzdG9ncmFtcyBwbG90IHF1YW50aXRhdGl2ZSBkYXRhIHdpdGggcmFuZ2VzIG9mIHRoZSBkYXRhIGdyb3VwZWQgaW50byBiaW5zIG9yIGludGVydmFscyB3aGlsZSBiYXIgY2hhcnRzIHBsb3QgY2F0ZWdvcmljYWwgZGF0YS4KCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KI2dncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IEFueGlldHksIHkgPSBFeGFtICkpICsgZ2VvbV9oaXN0b2dyYW0oKQojIHRoZSBjb2RlIGFib3ZlIGdpdmVzIGFuIGVycm9yIGFzIGdlb21faGlzdG9ncmFtIGNhbiBvbmx5IGhhdmUgeCBvciB5IGF4aXMgaW4gaXRzIGFlcygpCgpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBBbnhpZXR5KSkgKyBnZW9tX2hpc3RvZ3JhbSgpCgpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHkgPSBBbnhpZXR5KSkgKyBnZW9tX2hpc3RvZ3JhbSgpCgpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBBbnhpZXR5KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzEpCgpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBBbnhpZXR5KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzEsIGZpbGwgPSAiZ3JlZW4iKQoKZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gQW54aWV0eSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMxLCBmaWxsID0gImdyZWVuIiwgY29sID0gInJlZCIpCmBgYAoKTGV0J3Mgc3RvcCB1c2luZyB0aGUgTXlfZ3JhcGggdmFyaWFibGUgYW5kIHdyaXRlIHRoZSB3aG9sZSBjb2RlIGZyb20gdGhlIHN0YXJ0IGFnYWluIGZvciBhIGJhciBjaGFydApgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTR9CmdncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IFNsZWVwX3F1YWxpdHkpKSsKICBnZW9tX2JhcigpCmBgYApCZWNhdXNlIHdlIHdhbnQgdG8gcGxvdCBhIHN1bW1hcnkgb2YgdGhlIGRhdGEgKHRoZSBtZWFuKSByYXRoZXIgdGhhbiB0aGUgcmF3IHNjb3JlcyB0aGVtc2VsdmVzLCB3ZSBoYXZlIHRvIHVzZSBhIHN0YXQuCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gU2xlZXBfcXVhbGl0eSwgeSA9IEV4YW0sIGZpbGwgPSBHZW5kZXIpKSsKICBnZW9tX2JhcihzdGF0ID0gInN1bW1hcnkiLCBmdW4gPSAibWVhbiIpCgoKZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gU2xlZXBfcXVhbGl0eSwgeSA9IEV4YW0sIGZpbGwgPSBHZW5kZXIpKSsKICBnZW9tX2JhcihzdGF0ID0gInN1bW1hcnkiLCBmdW4gPSAibWVhbiIsIHBvc2l0aW9uID0gImRvZGdlIikKYGBgCgpUaGUgb3RoZXIgd2F5IHRvIGdldCB0aGUgc2FtZSBwbG90IHRoYXQgdGhlIGNvZGUgYWJvdmUgZ2l2ZXMsIGlzIHVzaW5nIHRoZSBzdGF0X3N1bW1hcnkgZnVuY3Rpb24gdGhhdCB0YWtlcyB0aGUgZm9sbG93aW5nIGdlbmVyYWwgZm9ybTogYHN0YXRfc3VtbWFyeShmdW5jdGlvbiA9IHgsIGdlb20gPSB5KWAKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gU2xlZXBfcXVhbGl0eSwgeSA9IEV4YW0sIGZpbGwgPSBHZW5kZXIpKSsKICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJiYXIiLCBwb3NpdGlvbiA9ICJkb2RnZSIpCmBgYAoKSG93IHRvIGNvbWJpbmUgbXVsdGlwbGUgcGxvdHM/IEhvdyB0byBjb21iaW5lIG11bHRpcGxlIHBsb3RzPyBXZSBjYW4gdXNlIHRoZSBgcGF0Y2h3b3JrYCBwYWNrYWdlLiBBIG5pY2UgdHV0b3JpYWwgb24gdXNpbmcgdGhpcyBwYWNrYWdlIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9wYXRjaHdvcmsuZGF0YS1pbWFnaW5pc3QuY29tL2FydGljbGVzL3BhdGNod29yay5odG1sKQoKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJBcnR3b3JrIGJ5IEFsbGlzb24gSG9yc3Q6IGh0dHBzOi8vZ2l0aHViLmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucyJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ3BhdGNod29ya18xLmpwZycpKQpgYGAKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9Nn0KcDEgPSBNeV9ncmFwaCArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gR2VuZGVyKSkgKyBnZW9tX3Ntb290aCgpCgpwMiA9IGdncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IEFueGlldHkpKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMSkKCnAzID0gZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gU2xlZXBfcXVhbGl0eSwgeSA9IEV4YW0sIGZpbGwgPSBHZW5kZXIpKSsKICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJiYXIiLCBwb3NpdGlvbiA9ICJkb2RnZSIpCgpwNCA9IE15X2dyYXBoICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoc2UgPSBGLCBtZXRob2QgPSBsbSkgKwogIGxhYnMoeCA9ICJFeGFtIHNjb3JlcyAlIiwgeSA9ICJBbnhpZXR5IHNjb3JlcyIpCgpjb21iaW5lZCA9IHAxICsgcDIrIHAzICsgcDQgKyBwbG90X2xheW91dChucm93ID0gNCwgYnlyb3cgPSBGKQoKY29tYmluZWQKCnAxIHwgcDIgLyBwMyAvIHA0CgpwMSB8IHAyIC8gKHAzIC8gcDQpCmBgYAoKCmBnZ3NhdmUoKWAgZnVuY3Rpb24sIHdoaWNoIGlzIGEgdmVyc2F0aWxlIGV4cG9ydGluZyBmdW5jdGlvbiB0aGF0IGNhbiBleHBvcnQgYXMgUG9zdFNjcmlwdCAoLmVwcy8ucHMpLCB0ZXggKHBpY3RleCksIHBkZiwganBlZywgdGlmZiwgcG5nLCBibXAsIHN2ZyBhbmQgd21mIChpbiBXaW5kb3dzIG9ubHkpLiBJbiBpdHMgYmFzaWMgZm9ybSwgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZnVuY3Rpb24gaXMgdmVyeSBzaW1wbGU6IGBnZ3NhdmUoZmlsZW5hbWUpYAoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD01fQpnZ3NhdmUoY29tYmluZWQsIGZpbGVuYW1lID0gaGVyZSgib3V0cHV0cyIsICJjb21iaW5lZC5wbmciKSwgZHBpPTMwMCkKYGBgCgoKTm93IHRoYXQgd2UgbGVhcm5lZCB0aGUgYmFzaWNzIG9mIGdncGxvdCwgbGV0J3MgZHJhdyBzb21lIHBsb3QgZm9yIG91ciBleHBlcmltZW50IGRhdGEuIEZpcnN0LCB3ZSBuZWVkIHRvIGNyZWF0ZSBhIGRhdGFzZXQgd2l0aCBhZ2dyZWdhdGVkIGBkZXByZXNzaW9uX3Njb3JlYCBzY29yZXMgb3ZlciBgZ3JvdXBgIGFuZCBgc3RhZ2VgLiBXZSB3aWxsIHVzZSB0aGlzIGRhdGFzZXQgZm9yIGxpbmUgYW5kIGJhciBncmFwaHMuCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTV9CmxpYnJhcnkoZ2dzY2kpCgpkYXRhX2V4cDFfb3JpZyA8LSByZWFkX2NzdihoZXJlKCJjbGVhbmVkX2RhdGEiLCJjbGVhbmVkX2RhdGFfZXhwMS5jc3YiKSkKCmRhdGFfZXhwMSA8LSBkYXRhX2V4cDFfb3JpZyU+JSAKICAjbXV0YXRlX2lmKGlzLmNoYXJhY3RlciwgZmFjdG9yKSAlPiUKICBtdXRhdGUoc3ViamVjdD0gZmFjdG9yKHN1YmplY3QpLCAjIGNvbnZlcnQgYWxsIGNoYXJhY3RlcnMgdG8gZmFjdG9yCiAgICAgICAgIGdyb3VwID0gZmFjdG9yKGdyb3VwKSwKICAgICAgICAgc3RhZ2UgPSBmYWN0b3Ioc3RhZ2UpKQoKCmFnZ3JlZ2F0ZWRfZGF0YV9leHAxIDwtIGRhdGFfZXhwMSAlPiUKICBncm91cF9ieShzdGFnZSwgZ3JvdXApICU+JQogIG11dGF0ZShkZXByZXNzaW9uX3Njb3JlID0gbWVhbihkZXByZXNzaW9uX3Njb3JlKSkgJT4lCiAgdW5ncm91cCgpCgoKYmFycGxvdF9leHAxIDwtIGFnZ3JlZ2F0ZWRfZGF0YV9leHAxICU+JQogIGdncGxvdChhZXMoeD1zdGFnZSwgeT0gZGVwcmVzc2lvbl9zY29yZSwgZmlsbD1ncm91cCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb249ICJkb2RnZSIpKwogIGxhYnMgKHg9ICcnLCB5PSAiRGVwcmVzc2lvbiBTY29yZSIpICsgCiAgdGhlbWVfYncoKSArIAogIHNjYWxlX2ZpbGxfamFtYSgpIAoKI2dnc2F2ZShiYXJwbG90X2V4cDEsIGZpbGVuYW1lID0gaGVyZSgib3V0cHV0cyIsImJhcnBsb3RfZXhwMS5wbmciKSwgZHBpPTMwMCkKCgpiYXJwbG90X2ZhY2V0X2V4cDEgPC0gYWdncmVnYXRlZF9kYXRhX2V4cDEgJT4lCiAgZ2dwbG90KGFlcyh4PWdyb3VwLCB5PSBkZXByZXNzaW9uX3Njb3JlLCBmaWxsPXN0YWdlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbj0gImRvZGdlIikrCiAgbGFicyAoeD0gJycsIHk9ICJEZXByZXNzaW9uIFNjb3JlIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMSksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKSArCiAgZmFjZXRfd3JhcCh+c3RhZ2UsIG5yb3cgPSAxKSsKICBzY2FsZV9maWxsX2pjbygpIAoKI2dnc2F2ZShiYXJwbG90X2ZhY2V0X2V4cDEsIGZpbGVuYW1lID0gaGVyZSgib3V0cHV0cyIsImJhcnBsb3RfZmFjZXRfZXhwMS5wbmciKSwgZHBpPTMwMCkKCgpsaW5lcGxvdF9leHAxIDwtIGFnZ3JlZ2F0ZWRfZGF0YV9leHAxICU+JQogIGdncGxvdChhZXMoeD1mYWN0b3Ioc3RhZ2UpLCB5PSBkZXByZXNzaW9uX3Njb3JlLCBncm91cD0gZ3JvdXAsIGNvbG9yPSBncm91cCkpICsKICBnZW9tX2xpbmUoYWVzKGxpbmV0eXBlPSBncm91cCkpICsKICBnZW9tX3BvaW50KHNpemU9IDUpKwogIGxhYnMgKHg9ICcnLCB5PSAiRGVwcmVzc2lvbiBTY29yZSIpICsgCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTEpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSkgKwogIHNjYWxlX2NvbG9yX25lam0oKSAKCiNnZ3NhdmUobGluZXBsb3RfZXhwMSwgZmlsZW5hbWUgPSBoZXJlKCJvdXRwdXRzIiwibGluZXBsb3RfZXhwMS5wbmciKSwgZHBpPTMwMCkKCgp2aW9saW5wbG90X2V4cDEgPC0gZGF0YV9leHAxICU+JQogIGdncGxvdChhZXMoeD1mYWN0b3Ioc3RhZ2UpLCB5PSBkZXByZXNzaW9uX3Njb3JlLCBmaWxsPSBncm91cCkpICsKICBnZW9tX3Zpb2xpbigpKwogIGxhYnMgKHg9ICcnLCB5PSAiRGVwcmVzc2lvbiBTY29yZSIpICsgCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMSkpICsKICBzY2FsZV9maWxsX2QzKCkgCgojZ2dzYXZlKHZpb2xpbnBsb3RfZXhwMSwgZmlsZW5hbWUgPSBoZXJlKCJvdXRwdXRzIiwidmlvbGlucGxvdF9leHAxLnBuZyIpLCBkcGk9MzAwKQoKCmJveHBsb3RfZXhwMSA8LSBkYXRhX2V4cDEgJT4lCiAgZ2dwbG90KGFlcyh4PWZhY3RvcihzdGFnZSksIHk9IGRlcHJlc3Npb25fc2NvcmUsIGZpbGw9IGdyb3VwKSkgKwogIGdlb21fYm94cGxvdCgpKwogICNnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGg9MC43NSksIGFscGhhPSAuNSkrCiAgbGFicyAoeD0gJycsIHk9ICJEZXByZXNzaW9uIFNjb3JlIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTExKSkgKwogIHNjYWxlX2ZpbGxfc2ltcHNvbnMoKSAKCiNnZ3NhdmUoYm94cGxvdF9leHAxLCBmaWxlbmFtZSA9IGhlcmUoIm91dHB1dHMiLCJib3hwbG90X2V4cDEucG5nIiksIGRwaT0zMDApCgoKYm94cGxvdF9mYWNldF9leHAxIDwtIGRhdGFfZXhwMSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZmFjdG9yKHN0YWdlKSwgeT0gZGVwcmVzc2lvbl9zY29yZSwgZmlsbD0gZ3JvdXApKSArCiAgZ2VvbV9ib3hwbG90KCkrCiAgbGFicyAoeD0gJycsIHk9ICJEZXByZXNzaW9uIFNjb3JlIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTExKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpICsKICBmYWNldF93cmFwKH5ncm91cCkrCiAgc2NhbGVfY29sb3Jfc2ltcHNvbnMoKSAKCiNnZ3NhdmUoYm94cGxvdF9mYWNldF9leHAxLCBmaWxlbmFtZSA9IGhlcmUoIm91dHB1dHMiLCJib3hwbG90X2ZhY2V0X2V4cDEucG5nIiksIGRwaT0zMDApCgpgYGAKCkxldCdzIGNvbWJpbmUgb3VyIHBsb3RzOgoKYGBge3IgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTl9Cgpjb21iaW5lZF9wbG90X2V4cDEgPC0gYmFycGxvdF9mYWNldF9leHAxIC8gKGxpbmVwbG90X2V4cDErdmlvbGlucGxvdF9leHAxK2JveHBsb3RfZXhwMSkKY29tYmluZWRfcGxvdF9leHAxCmBgYAoKQW5kIGhlcmUsIHdlIHNhdmUgb3VyIHBsb3RzIHRvIHRoZSBgb3V0cHV0c2AgZm9sZGVyLgpgYGB7cm1lc3NhZ2U9Rn0KZ2dzYXZlKGNvbWJpbmVkX3Bsb3RfZXhwMSwgZmlsZW5hbWUgPSBoZXJlKCJvdXRwdXRzIiwiY29tYmluZWRfcGxvdF9leHAxLnBuZyIpLCBkcGk9MzAwLCB3aWR0aCA9IDEyKQpgYGAKCi0gW0EgY29tcGxldGUgZ3VpZGUgdG8gZ2dwbG90XShodHRwczovL3d3dy5jZWRyaWNzY2hlcmVyLmNvbS8yMDE5LzA4LzA1L2EtZ2dwbG90Mi10dXRvcmlhbC1mb3ItYmVhdXRpZnVsLXBsb3R0aW5nLWluLXIvKQoKIyBEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzCgpgYGB7ciBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjcwMHB4Iiwgb3V0LmhlaWdodD0iMzUwcHgiLCBmaWcuY2FwPSAiQXJ0d29yayBieSBBbGxpc29uIEhvcnN0OiBodHRwczovL2dpdGh1Yi5jb20vYWxsaXNvbmhvcnN0L3N0YXRzLWlsbHVzdHJhdGlvbnMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCdub3Rfbm9ybWFsLnBuZycpKQpgYGAKCk5vdywgbGV0J3MgZG8gc29tZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzLiBOb3csIHdlIGNhbiBvcGVuIGEgbmV3IHNjcmlwdCBjYWxsZWQgYGRhdGFfYW5hbHlzaXMucmAgYW5kIHJlYWQgc29tZSBkYXRhc2V0cy4gVGhlbiB3ZSB1c2UgYHNraW1yYCBwYWNrYWdlIHRvIGRlc2NyaWJlIG91ciBkYXRhLgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsfQpuYXJjaXNzaXNtX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwibmFyY2lzc2lzbV9kYXRhLmNzdiIpKQpuYXJjaXNzaXNtX2RhdGEgJT4lIHNraW1yOjpza2ltKCkKYGBgCgoqICpFeGVyY2lzZSo6IE9wZW4gdGhlIGRhdGFzZXQgY2FsbGVkIGB0cmVhdG1lbnRfZGF0YS5jc3ZgIGFuZCBkbyBhIGRlc2NyaXB0aXZlIGFuYWx5c2lzOgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KdHJlYXRtZW50X2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwidHJlYXRtZW50X2RhdGEuY3N2IikpCnRyZWF0bWVudF9kYXRhICU+JSBza2ltcjo6c2tpbSgpCmBgYAoKKiAqRXhlcmNpc2UqOiBEbyB0aGUgc2FtZSB0aGluZyBmb3IgdGhlIGBtZW1vcnlfZGF0YS5jc3ZgLgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9Cm1lbW9yeV9kYXRhIDwtIHJlYWRfY3N2KGhlcmUoImNsZWFuZWRfZGF0YSIsIm1lbW9yeV9kYXRhLmNzdiIpKQptZW1vcnlfZGF0YSAlPiUgZ3JvdXBfYnkodGltZSkgJT4lCiAgc2tpbXI6OnNraW0oKQpgYGAKCgoqICpFeGVyY2lzZSo6IEZvciB0aGlzIGV4ZXJjaXNlLCB3ZSB1c2UgYSBkYXRhc2V0IG9mIG9uZSBvZiBteSBvd24gc3R1ZGllcy4gSW4gdGhpcyBzdHVkeSwgd2UgYXNrZWQgcGFydGljaXBhbnRzIHRvIGd1ZXNzIHRoZSBwaHlzaWNhbCBicmlnaHRuZXNzIG9mIHJlYXNvbmluZyBhcmd1bWVudHMgYW5kIHRoZW4gd2UgZ2F2ZSBhIGNvZ25pdGl2ZSBhYmlsaXR5IHRlc3QuIChTZWUgdGhlIG9yaWdpbmFsIHN0dWR5IFtoZXJlXShodHRwczovL29zZi5pby9lYnhuZi8pKS4gT3BlbiBgZ2hhc2VtaV9icmlnaHRuZXNzX2V4cDQuY3N2YCBmaWxlIGFuZCBhbnN3ZXIgdG8gdGhlIGZvbGxvd2luZyBxdWVzdGlvbnM6CgoxLiBIb3cgbWFueSBwYXJ0aWNpcGFudHMgZGlkIHdlIHRlc3QgaW4gdG90YWw/CjIuIEZpbmQgb3V0IGhvdyBtYW55IG1hbGUgYW5kIGZlbWFsZSB3ZSB0ZXN0ZWQuCjMuIENhbGN1bGF0ZSBtZWFuIGFuZCBzZCBmb3IgYWdlIGFuZCBjb2duaXRpdmUgYWJpbGl0eSAoYGNvZ19hYmlsaXR5YCkuCgoKYGBge3Igd2FybmluZz1GLCBtZXNzYWdlPUZ9CmdoYXNlbWlfZGF0YSA8LSByZWFkX2NzdihoZXJlKCJjbGVhbmVkX2RhdGEiLCJnaGFzZW1pX2JyaWdodG5lc3NfZXhwNC5jc3YiKSkKCmdoYXNlbWlfZGF0YSAlPiUgc3VtbWFyaXNlKG4gPSBuX2Rpc3RpbmN0KHBhcnRpY2lwYW50KSkgIyBudW1iZXIgb2YgcGFydGljaXBhbnRzOjIwMAoKZ2hhc2VtaV9kYXRhICU+JSBncm91cF9ieSAocGFydGljaXBhbnQpICU+JSBmaWx0ZXIgKHJvd19udW1iZXIoKT09MSkgJT4lIGdyb3VwX2J5IChnZW5kZXIpICU+JSBzdW1tYXJpc2Uobj0gbigpKSAlPiUgdW5ncm91cCgpICMgMTgzIGZlbWFsZSwgMTcgbWFsZQoKZ2hhc2VtaV9kYXRhICU+JSBkcGx5cjo6c2VsZWN0IChhZ2UsIGNvZ19hYmlsaXR5KSAlPiUgc2tpbXI6OnNraW0oKSAjIG1lYW4gYW5kIHNkIGZvciBhZ2UgYW5kIGNvZ25pdGl2ZSBhYmlsaXR5CmBgYAoKCgojIERhdGEgQW5hbHlzaXMKCiMjIHQtdGVzdAoKTm93LCB3ZSB1c2UgdGhlIHRyZWF0bWVudCBkYXRhIHRvIHJ1biB0aHJlZSBkaWZmZXJlbnQgaW5kZXBlbmRlbnQgdC10ZXN0cy4gU3VwcG9zZSB3ZSBkaWQgYW4gZXhwZXJpbWVudCB0byBjb21wYXJlIHRoZSBlZmZlY3RpdmVuZXNzIG9mIENCVCB2cy4gUHN5Y2hvZHluYW1pYyB0aGVyYXBpZXMgaW4gZGVjcmVhc2luZyBhbnhpZXR5LCBhbmQgZGVwcmVzc2lvbiBhbmQgYWxzbyBpbiBpbXByb3ZpbmcgbGlmZSBzYXRpc2ZhY3Rpb246CgpgYGB7cn0KIyB0LnRlc3QgKGluZGVwKQp0LnRlc3QoYW54aWV0eX50cmVhdG1lbnQsIGRhdGE9IHRyZWF0bWVudF9kYXRhKQp0LnRlc3QoZGVwcmVzc2lvbn50cmVhdG1lbnQsIGRhdGE9IHRyZWF0bWVudF9kYXRhKQp0LnRlc3QobGlmZV9zYXRpc2ZhY3Rpb25+dHJlYXRtZW50LCBkYXRhPSB0cmVhdG1lbnRfZGF0YSkKYGBgCgpJbiBhbm90aGVyIGV4cGVyaW1lbnQsIHN1cHBvc2Ugd2UgaGF2ZSBjcmVhdGVkIGEgbWV0aG9kIHRvIGJvb3N0IG1lbW9yeS4gVGhlbiwgd2UgcmVjcnVpdCBzb21lIHBhcnRpY2lwYW50cywgZG8gYSBtZW1vcnkgcHJlLXRlc3QsIGltcGxlbWVudCB0aGUgbWV0aG9kLCBhbmQgZG8gYSBtZW1vcnkgcG9zdC10ZXN0LCBOb3csIHdlIHdhbnQgdG8gc2VlIHdoZXRoZXIgb3VyIG1ldGhvZCBoYXZlIGltcHJvdmVkIHBhcnRpY2lwYW50cycgbWVtb3J5OiAKCmBgYHtyfQojIHQudGVzdCAocGFpcmVkKQp0LnRlc3QobWVtb3J5X3Njb3JlfnRpbWUsIGRhdGE9IG1lbW9yeV9kYXRhLCBwYWlyZWQ9IFQpCmBgYAoKTm93IHRoYXQgd2UgbGVhcm5lZCBhYm91dCB0LXRlc3QsIGxldCdzIHBlcmZvcm0gdGhpcyB0ZXN0IG9uIG91ciBkYXRhc2V0LiBJcyB0aGVyZSBhIGRpZmZlcmVuY2UgYmV0d2VlbiBncm91cHMgYXQgdGhlIGZpcnN0IHN0YWdlPyBJZGVhbGx5LCB3ZSB3YW50IHBhcnRpY2lwYW50cycgZGVwcmVzaW9uIHNjb3JlIGF0IHRoZSBmaXJzdCBzdGFnZSB0byBiZSBzaW1pbGFyIGZvciBib3RoIGdyb3VwcyBiZWNhdXNlIHdlIGhhdmUgbm90IHN0YXJ0ZWQgb3VyIHRyZWF0bWVudCB5ZXQuIFByZXZpb3VzIGdyYXBocyBzaG93ZWQgdXMgdGhhdCBkZXByZXNzaW9uIHNjb3JlcyBvZiB0aGUgQ0JUIGFuZCBQc3ljaG9keW5hbWljIGdyb3VwcyBhdCB0aGlzIHN0YWdlIGFyZSBwcmV0dHkgY2xvc2UuIExldCdzIHRlc3QgdGhhdCB1c2luZyBhbiAqKmluZGVwZW5kZW50IHQtdGVzdCoqIChiZWNhdXNlIHdlIGhhdmUgMiBpbmRlcGVuZGVudCBncm91cHMpOgoKYGBge3J9CiMgSXMgdGhlcmUgYSBkaWZmZXJlbmNlIGJldHdlZW4gZ3JvdXBzIGF0IHRoZSBmaXJzdCBzdGFnZT8KZGF0YV9leHAxICU+JSAKICBncm91cF9ieShncm91cCkgJT4lIAogIGZpbHRlcihzdGFnZT09J3N0YWdlMScpICU+JSAKICB1bmdyb3VwICgpICU+JQogIHQudGVzdChkZXByZXNzaW9uX3Njb3Jlfmdyb3VwLCBkYXRhID0gLiwgcGFpcmVkPUZBTFNFKQpgYGAKCk5vdywgd2Ugd29uZGVyIGlmIHBzeWNob3RoZXJhcHkgdHJlYXRtZW50cyB3ZXJlIGVmZmVjdGl2ZSBhdCBhbGwsIHJlZ2FyZGxlc3Mgb2YgdGhlIHRyZWF0bWVudCBtZXRob2QuIFNvLCB3ZSB3b3VsZCBsaWtlIHRvIHRlc3QgaWYgZGVwcmVzaW9uIHNjb3JlIGF0IHRoZSBmb3J0aCBzdGFnZSBhcmUgbG93ZXIgdGhhbiBzY29yZXMgYXQgdGhlIHN0YWdlIDI/IFNpbmNlIGEgcGFpciBvZiBzY29yZSBhdCBzdGFnZSAyIGFuZCBzdGFnZSA0IGlzIGNvbWluZyBmcm9tIGEgc2FtZSBwZXJzb24sIHdlIHVzZSAqKnBhaXJlZCB0LXRlc3QqKi4KCmBgYHtyfQojIElzIHRoZXJlIGEgZGlmZmVyZW5jZSBiZXR3ZWVuIHJhdGluZ3Mgb2Ygc3RhZ2UyIGFuZCBzdGFnZTQ/CmRhdGFfZXhwMSAlPiUgCiAgZmlsdGVyKHN0YWdlPT0nc3RhZ2UyJyB8IHN0YWdlPT0nc3RhZ2U0JykgJT4lIAogIHVuZ3JvdXAgKCkgJT4lCiAgdC50ZXN0KGRlcHJlc3Npb25fc2NvcmV+c3RhZ2UsIGRhdGEgPSAuLCBwYWlyZWQ9VFJVRSkKYGBgCgoKKiAqRXhlcmNpc2UqOiBKb2huIGV0IGFsLiAoMjAxOSkgaW52ZXN0aWdhdGVkIHRoZSBjb25zZXF1ZW5jZXMgb2YgYmFja2luZyBkb3duIChjaGFuZ2luZyBvbmUncyBtaW5kIGluIGxpZ2h0cyBvZiBldmlkZW5jZSlhbmQgaG93IG90aGVyIHBlb3BsZSB2aWV3IHNvbWVvbmUgd2hvIGNoYW5nZSB0aGVpciBtaW5kLiBJbiB0aGVpciBzZWNvbmQgZXhwZXJpbWVudHMsIHRoZXkgcHJlc2VudGVkIHBhcnRpY2lwYW50cyBlaXRoZXIgd2l0aCBhIHBlcnNvbiB3aG8gY2hhbmdlcyB0aGVpciBtaW5kIG9yIGEgcGVyc29uIHdobyByZWZ1c2VzIHRvIGJhY2sgZG93bi4gVGhlbiwgdGhleSBhc2tlZCBwYXJ0aWNpcGFudHMgdG8gcmF0ZSBob3cgaW50ZWxsaWdlbnQgYW5kIGNvbmZpZGVudCB0aGUgcGVyc29uIGlzIChTZWUgdGhlIG9yaWdpbmFsIHN0dWR5IFtoZXJlXShodHRwczovL3d3dy5oYnMuZWR1L2ZhY3VsdHkvUHVibGljYXRpb24lMjBGaWxlcy9Kb2huJTIwZXQlMjBhbCUyMC0lMjBzZWxmLXByZXNlbnRhdGlvbmFsJTIwY29uc2VxdWVuY2VzX2I4NWIyYzQzLWE1YjUtNDc0Yy05ZTJjLWU5ODUzYjEwNzI3ZS5wZGYpKS4gVGhleSByZXBvcnRlZCB0aGF0OiAKCj4gIlJlbGF0aXZlIHRvIHRoZSBlbnRyZXByZW5ldXIgd2hvIGRpZCBub3QgYmFjayBkb3duLCBwYXJ0aWNpcGFudHMganVkZ2VkIHRoZSBlbnRyZXByZW5ldXIgd2hvIGJhY2tlZCBkb3duIGFzIG1vcmUgaW50ZWxsaWdlbnQgKE1fYmFja2VkX2Rvd249NS4xMyBvdXQgb2YgNywgU0Q9MS4wOTsgTV9kaWRfbm90X2JhY2tfZG93bj0zLjk3LCBTRD0xLjU0OyB0KDI3MS4xMik94oiSNy41OSwgcCA8IC4wMDEpIGJ1dCBsZXNzIGNvbmZpZGVudCAoTV9iYWNrZWRfZG93bj00LjUwIG91dCBvZiA3LCBTRD0xLjM2OyBNX2RpZF9ub3RfYmFja19kb3duPTUuNjUsIFNEPTEuMTA7IHQoMjkxLjAxKT04LjA4LCBwIDwgLjAwMSkuIi4KCk9wZW4gdGhlIGBqb2huX2JhY2tkb3duX2V4cDIuY3N2YCBmaWxlIGFuZCB0cnkgdG8gcmVwcm9kdWNlIHRoZWlyIHJlc3VsdHMuIFJ1biB0d28gc2VwYXJhdGUgaW5kZXBlbmRlbnQgdC10ZXN0LCBvbmUgd2l0aCBgaW50ZWxsaWdlbnRgIGFzIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgYW5kIG9uZSB3aXRoIGBjb25maWRlbnRgIGFzIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUuIEZvciBib3RoIHQtdGVzdCwgdXNlIGBiYWNrX2Rvd25gIGFzIHRoZSBiZXR3ZWVuLXN1YmplY3QgaW5kZXBlbmRlbnQgdmFyaWFibGUuCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0Kam9obl9kYXRhIDwtIHJlYWRfY3N2KGhlcmUoImNsZWFuZWRfZGF0YSIsImpvaG5fYmFja2Rvd25fZXhwMi5jc3YiKSkKCgp0LnRlc3QoaW50ZWxsaWdlbnR+YmFja19kb3duLCBkYXRhID0gam9obl9kYXRhLCBwYWlyZWQ9RkFMU0UpCnQudGVzdChjb25maWRlbnR+YmFja19kb3duLCBkYXRhID0gam9obl9kYXRhLCBwYWlyZWQ9RkFMU0UpCmBgYAoKCiMjIEFuYWx5c2lzIG9mIFZhcmlhbmNlIChBTk9WQSkKCk5vdywgbGV0J3MgYW5hbHlzaXMgb3VyIG1haW4gZXhwZXJpbWVudCBkYXRhOiBEbyBwYXJ0aWNpcGFudHMgaW4gdGhlIENCVCBncm91cCBzaG93IGJldHRlciBvdXRjb21lIGNvbXBhcmVkIHRvIHBhcnRpY2lwYW50cyBpbiB0aGUgUHN5Y2hvZHluYW1pYyBncm91cD8gU3VwcG9zZSB3ZSBiZWxpZXZlIHRoYXQgcGFydGljaXBhbnRzIHNob3VsZCBzaG93IGxvd2VyIGRlcHJlc3Npb24gYWZ0ZXIgNSBvciAxMCBzZXNzaW9ucyBvZiBib3RoIHBzeWNob3RoZXJhcHkgdHJlYXRtZW50cyBhbmQgdGhpcyBkZWNyZWFzZSBzaG91bGQgYmUgbW9yZSBwcm9ub3VuY2VkIGZvciBDQlQgdGhhbiBwc3ljaG9keW5hbWljIHBzeWNob3RoZXJhcHkuIElmIHRoaXMgaXMgdGhlIGNhc2UuIHdlIGV4cGVjdCBhbiBpbnRlcmFjdGlvbiBpbiB0aGUgdHJhZGl0aW9uYWwgKipBbmFseXNpcyBvZiBWYXJpYW5jZSAoQU9OVkEpKiogdGVzdC4KCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQphb3ZfbTEgPC0gYW92X2NhciAoZGVwcmVzc2lvbl9zY29yZSB+IGdyb3VwKnN0YWdlICsKICAgICAgICAgICAgICAgICAgICAgRXJyb3Ioc3ViamVjdC9zdGFnZSksIGRhdGEgPSBkYXRhX2V4cDEpICAKYGBgCgpgYGB7ciBlY2hvPUZ9CmtuaXRyOjprYWJsZShuaWNlKGFvdl9tMSkpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBUKQpgYGAKCkFzIHlvdSBjYW4gc2VlLCB3ZSBmb3VuZCBhIHNpZ25pZmljYW50IG1haW4gZWZmZWN0IG9mIHN0YWdlIGFuZCBhIHNpZ25pZmljYW50IGdyb3VwIGJ5IHN0YWdlIGludGVyYWN0aW9uLiBXZSBjYW4gdXNlIHRoZSBgZW1tZWFuc2AgcGFja2FnZSB0byBkbyBwb3N0LWhvYyB0ZXN0cy4KCmBgYHtyIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQojIG1haW4gZWZmZWN0IG9mIHN0YWdlCmVtbWVhbnMoYW92X20xLCAnc3RhZ2UnKQpwYWlycyhlbW1lYW5zKGFvdl9tMSwgJ3N0YWdlJyksIGFkanVzdD0gJ2hvbG0nKQpgYGAKCgpgYGB7ciB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0KIyBncm91cCBieSBzdGFnZSBpbnRlcmFjdGlvbgplbW1lYW5zKGFvdl9tMSwgImdyb3VwIiwgYnk9ICJzdGFnZSIpCnVwZGF0ZShwYWlycyhlbW1lYW5zKGFvdl9tMSwgImdyb3VwIiwgYnk9ICJzdGFnZSIpKSwgYnkgPSBOVUxMLCBhZGp1c3QgPSAiaG9sbSIpIApgYGAKCllvdSBjYW4gdXNlIHRoZSBgYWZleF9wbG90YCBmdW5jdGlvbiBmcm9tIGFmZXggdG8gY3JlYXRlIGJlYXV0aWZ1bCBwbG90cy4gVGhvc2UgcGxvdHMgaW50ZXJhY3RzIG5pY2VseSB3aXRoIGdncGxvdDoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwfQphZmV4X3Bsb3QoYW92X20xLCB4ID0gInN0YWdlIiwgdHJhY2UgPSAiZ3JvdXAiLCBlcnJvcj0nYmV0d2VlbicsCiAgICAgICAgICBsaW5lX2FyZyA9IGxpc3Qoc2l6ZT0xKSwKICAgICAgICAgIHBvaW50X2FyZyA9IGxpc3Qoc2l6ZT0zLjUpLAogICAgICAgICAgZGF0YV9hcmcgPSBsaXN0KHNpemU9IDEsIGNvbG9yPSAnZ3JleScsIHdpZHRoPS40KSwKICAgICAgICAgIGRhdGFfZ2VvbSA9IGdlb21fYm94cGxvdCwKICAgICAgICAgIG1hcHBpbmcgPSBjKCJsaW5ldHlwZSIsICJzaGFwZSIsICJmaWxsIiksCiAgICAgICAgICBsZWdlbmRfdGl0bGUgPSAiR3JvdXAiKSArCiAgbGFicyh5ID0gIkRlcHJlc3Npb24gU2NvcmUiLCB4ID0gIiIpICsKICB0aGVtZV9idygpKyAjIHJlbW92ZSB0aGUgZ3JleSBiYWNrZ3JvdW5kIGFuZCBncmlkCiAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEzKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMyksCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTMpLAogICAgICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMyksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLAogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMSwgImNtIiksCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gJ2JsYWNrJywgZmlsbCA9ICd3aGl0ZScsIGxpbmV0eXBlPSdzb2xpZCcpKSsKICBzY2FsZV9jb2xvcl9zaW1wc29ucygpICsKICBzY2FsZV9maWxsX3NpbXBzb25zKCkKYGBgCgoKSWYgeW91IGFyZSBpbnRlcmVzdGVkIGluIHRoaXMgdG9waWMsIGNoZWNrIG91dCB0aGlzIG5pY2UgdHV0b3JpYWwgYWJvdXQgW3VzaW5nIGFmZXggdG8gcnVuIEFOT1ZBXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvYWZleC92aWduZXR0ZXMvYWZleF9hbm92YV9leGFtcGxlLmh0bWwpLCBhbmQgYWxzbyB0aGlzIGludGVyZXN0aW5nIHR1dG9yaWFsIG9uIHRoZSBbZW1tZWFucyBwYWNrYWdlXShodHRwczovL2Fvc21pdGgucmJpbmQuaW8vMjAxOS8wMy8yNS9nZXR0aW5nLXN0YXJ0ZWQtd2l0aC1lbW1lYW5zLykuCgoqICpFeGVyY2lzZSo6IFJvdGVsbG8gZXQgYWwuICgyMDE4KSBpbnZlc3RpZ2F0ZWQgdGhlIGFzc29jaWF0aW9uIGJldHdlZW4gdGhlIHJhY2UgKFdoaXRlIHZzLiBCbGFjayBmYWNlcykgYW5kIHRoZSBndW4tdG9vbCBqdWRnbWVudHMuIEluIHRoZWlyIGZpcnN0IGV4cGVyaW1lbnRzLCB0aGV5IHByZXNlbnRlZCBwYXJ0aWNpcGFudHMgd2l0aCAxNiBXaGl0ZSBtYWxlIGZhY2VzIGFuZCAxNiBCbGFjayBtYWxlIGZhY2VzLCBhbmQgZm9sbG93aW5nIHRoYXQgOCBpbWFnZXMgb2YgZ3VucyBhbmQgOCBpbWFnZXMgb2YgdG9vbHMuIFRoZXkgYXNrZWQgcGFydGljaXBhbnRzIHRvIGp1ZGdlIGlmIHRoZSBvYmplY3QgaXMgYSB0b29sIG9yIGEgZ3VuIGJ5IHByZXNzaW5nIGtleWJvYXJkIGJ1dHRvbnMuIFRoZW4sIHRoZXkgcmFuIGFuIEFOT1ZBIHRvIHNlZSBpZiBwYXJ0aWNpcGFudHMnIGd1biByZXNwb25zZXMgYXJlIGhpZ2hlciBmb3IgYW55IG9mIHRoZSByYWNlcy4gU28sIHRoZXkgaW5jbHVkZWQgcHJpbWUgcmFjZSAoQmxhY2ssIFdoaXRlKSBhbmQgdGFyZ2V0IGlkZW50aXR5IChndW4sIHRvb2wpIGFzIGluZGVwZW5kZW50IHZhcmlhYmxlcyBhbmQgcGFydGljaXBhbnRzJyBndW4gcmVzcG9uc2VzIGFzIGRlcGVuZGVudCB2YXJpYWJsZSBpbnRvIHRoZWlyIGxpbmVhciBtb2RlbCAoU2VlIHRoZSBvcmlnaW5hbCBzdHVkeSBbaGVyZV0oaHR0cHM6Ly9vbmxpbmUudWNwcmVzcy5lZHUvY29sbGFicmEvYXJ0aWNsZS80LzEvMzIvMTEyOTg2L1RoZS1TaGFwZS1vZi1ST0MtQ3VydmVzLWluLVNob290ZXItVGFza3MpKS4gVGhleSBmb3VuZCB0aGF0OiAKCj4gIlBhcnRpY2lwYW50cyBtYWRlIG1vcmUgZ3VuIHJlc3BvbnNlcyB0byBndW5zIHRoYW4gdG8gdG9vbHMsIEYoMSw0NSkgPSA1MzI0MywgcCA8IDAuMDAwMSwgzrcyZyA9IDAuOTk4LiBIb3dldmVyLCB0aGUgcmFjZSBvZiB0aGUgcHJpbWUgZmFjZSBkaWQgbm90IG1hdHRlciwgRigxLDQ1KSA9IDAuMjg3LCBwID4gMC41OSwgzrcyZyA9IDAuMDAxLCBub3Igd2FzIHRoZXJlIGFuIGludGVyYWN0aW9uIG9mIHByaW1lIHJhY2Ugd2l0aCB0YXJnZXQgb2JqZWN0LCBGKDEsNDUpID0gMC4wMjIsIHAgPiAwLjg4LCDOtzJnID0gMC4wMDApIi4KCk9wZW4gdGhlIGByb3RlbGxvX3Nob290ZXJfZXhwMS5jc3ZgIGZpbGUgYW5kIHRyeSB0byByZXByb2R1Y2UgdGhlaXIgcmVzdWx0cy4gUnVuIGFuIEFOT1ZBICh0eXBlIElJSSkgd2l0aCBgcmVzcGAgYXMgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBhbmQgdGFyZ2V0LCBwcmltZSwgYW5kIHRoZWlyIGludGVyYWN0aW9uIGFzIGluZGVwZW5kZW50IHZhcmlhYmxlcy4KCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KIyBsb2FkIHRoZSBnZW5lcmFsIGRhdGEgZmlsZQpyb3RlbGxvX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwicm90ZWxsb19zaG9vdGVyX2V4cDEuY3N2IikpCgojIEFOT1ZBCnJvdGVsbG9fYW92IDwtIGFvdl9jYXIgKHJlc3AgfiB0YXJnZXQqcHJpbWUgKwogICAgICAgICAgIEVycm9yKHN1YmplY3QvdGFyZ2V0KnByaW1lKSwgZGF0YSA9IHJvdGVsbG9fZGF0YSkKYGBgCgpgYGB7ciBlY2hvPUZ9CmtuaXRyOjprYWJsZShuaWNlKHJvdGVsbG9fYW92KSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCmBgYAoKCgojIyBDb3JyZWxhdGlvbgoKSGVyZSwgd2Ugd2FudCB0byBjaGVjayB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB2YXJpYWJsZXMgb24gdGhlIGBuYXJjaXNzaXNtX2RhdGFgLiBGaXJzdCwgd2UgbmVlZCB0byByZW1vdmUgYHN1YmplY3RgIGNvbHVtbiBiZWNhdXNlIGl0IGlzIG5vdCBudW1lcmljOgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KbmFyY2lzc2lzbV9kYXRhX2NvciA8LSBuYXJjaXNzaXNtX2RhdGEgJT4lCiAgc2VsZWN0KC1zdWJqZWN0KQpgYGAKCmBgYHtyIG1lc3NhZ2U9RiwgZXZhbD1GLCBmaWcuYWxpZ249J2NlbnRlcicsIGRwaT0zMDB9CgojLS0gQmFzZSBSOgpjb3IobmFyY2lzc2lzbV9kYXRhX2NvciwgbWV0aG9kID0gInBlYXJzb24iLCAgdXNlID0gImNvbXBsZXRlLm9icyIpCgojLS0gUHN5Y2ggbGlicmFyeToKcHN5Y2g6OnBhaXJzLnBhbmVscyhuYXJjaXNzaXNtX2RhdGFfY29yLCBtZXRob2QgPSAicGVhcnNvbiIsIGhpc3QuY29sID0gIiMwMEFGQkIiLCBkZW5zaXR5ID0gVCwgZWxsaXBzZXMgPSBGLCBzdGFycyA9IFQpCgojLS0gQ29ycmVsYXRpb24gbGlicmFyeToKIyBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJlYXN5c3RhdHMvY29ycmVsYXRpb24iKQojbGlicmFyeSgiY29ycmVsYXRpb24iKQpjb3JyZWxhdGlvbjo6Y29ycmVsYXRpb24obmFyY2lzc2lzbV9kYXRhX2NvcikgJT4lIHN1bW1hcnkoKQoKIy0tIGFwYVRhYmxlcyBsaWJyYXJ5OgpuYXJjaXNzaXNtX2RhdGFfY29yICU+JSAKICBhcGFUYWJsZXM6OmFwYS5jb3IudGFibGUoZmlsZW5hbWU9Ii4vb3V0cHV0cy9Db3JNYXRyaXguZG9jIiwgc2hvdy5jb25mLmludGVydmFsPVQpCmBgYAoKYGBge3IgbWVzc2FnZT1GLCBlY2hvPUYsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMH0KCiMtLSBCYXNlIFI6CmNvcihuYXJjaXNzaXNtX2RhdGFfY29yLCBtZXRob2QgPSAicGVhcnNvbiIsICB1c2UgPSAiY29tcGxldGUub2JzIiklPiUKICBrbml0cjo6a2FibGUoZGlnaXRzID0gMikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IEYpCgojLS0gUHN5Y2ggbGlicmFyeToKcHN5Y2g6OnBhaXJzLnBhbmVscyhuYXJjaXNzaXNtX2RhdGFfY29yLCBtZXRob2QgPSAicGVhcnNvbiIsIGhpc3QuY29sID0gIiMwMEFGQkIiLCBkZW5zaXR5ID0gVCwgZWxsaXBzZXMgPSBGLCBzdGFycyA9IFQpCgojLS0gQ29ycmVsYXRpb24gbGlicmFyeToKIyBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJlYXN5c3RhdHMvY29ycmVsYXRpb24iKQojbGlicmFyeSgiY29ycmVsYXRpb24iKQpjb3JyZWxhdGlvbjo6Y29ycmVsYXRpb24obmFyY2lzc2lzbV9kYXRhX2NvcikgJT4lIHN1bW1hcnkoKSU+JQogIGtuaXRyOjprYWJsZShkaWdpdHMgPSAyKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gRikKCmBgYAoKCiogKkV4ZXJjaXNlKjogUGVubnljb29rIGV0IGFsLiAoMjAyMCkgaW52ZXN0aWdhdGVkIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBhY3RpdmVseSBvcGVuLW1pbmRlZCB0aGlua2luZyBzdHlsZSBhYm91dCBldmlkZW5jZSAoQU9ULUUpIGFuZCBkaWZmZXJlbnQgcG9saXRpY2FsLCBzY2llbnRpZmljLCBhbmQgcmVsaWdpb3VzIGJlbGllZnMgKHNlZSB0aGUgb3JpZ2luYWwgcGFwZXIgW2hlcmVdKGh0dHBzOi8vcHN5YXJ4aXYuY29tL2E3azk2KSkuIEluIHRoZWlyIGZpcnN0IGV4cGVyaW1lbnQsIHRoZXkgY2FsY3VsYXRlZCB0aGUgY29ycmVsYXRpb24gb2YgQU9URSBhbmQgc2NpZW50aWZpYyBiZWxpZWZzIGl0ZW1zIChnbG9iYWwgd2FybWluZywgZXZvbHV0aW9uLCBldGMuKSBhbmQgdGhleSBmb3VuZCB0aGUgZm9sbG93aW5nIHJlc3VsdHM6CgpgYGB7ciBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjcwMHB4Iiwgb3V0LmhlaWdodD0iMzUwcHgiLCBmaWcuY2FwPSAiYWRhcHRlZCBmcm9tIFtQZW5ueWNvb2sgZXQgYWwuICgyMDIwKV0oaHR0cHM6Ly9wc3lhcnhpdi5jb20vYTdrOTYpIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgnaW5wdXRzJywncGVubnljb29rX2NvcnIucG5nJykpCmBgYAoKT3BlbiB0aGUgYHBlbm55Y29va19hb3RlX2V4cDEuY3N2YCBmaWxlIGFuZCB0cnkgdG8gcmVwcm9kdWNlIHRoZWlyIHJlc3VsdHMgYnkgY3JlYXRpbmcgdGhlIHNhbWUgY29ycmVsYXRpb24gbWF0cml4LgoKYGBge3IgbWVzc2FnZT1GLCBldmFsPUZ9CnBlbm55Y29va19kYXRhIDwtIHJlYWRfY3N2KGhlcmUoImNsZWFuZWRfZGF0YSIsInBlbm55Y29va19hb3RlX2V4cDEuY3N2IikpIAoKCiMtLS0tLS0tLS0tIEJhc2UgUjoKY29yKHBlbm55Y29va19kYXRhLCBtZXRob2QgPSAicGVhcnNvbiIsICB1c2UgPSAiY29tcGxldGUub2JzIikKCiMtLS0tLS0tLS0tIFBzeWNoIGxpYnJhcnk6CnBlbm55Y29va19kYXRhICU+JSAKICBwc3ljaDo6cGFpcnMucGFuZWxzKG1ldGhvZCA9ICJwZWFyc29uIiwgaGlzdC5jb2wgPSAiIzAwQUZCQiIsIGRlbnNpdHkgPSBULCBlbGxpcHNlcyA9IEYsIHN0YXJzID0gVCkKCiMtLS0tLS0tLS0tIENvcnJlbGF0aW9uIGxpYnJhcnk6CmNvcnJlbGF0aW9uOjpjb3JyZWxhdGlvbihwZW5ueWNvb2tfZGF0YSkgJT4lIHN1bW1hcnkoKQoKIy0tLS0tLS0tLS0gYXBhVGFibGVzIGxpYnJhcnk6CnBlbm55Y29va19kYXRhICU+JSAKICBhcGFUYWJsZXM6OmFwYS5jb3IudGFibGUoZmlsZW5hbWU9Ii4vb3V0cHV0cy9Db3JNYXRyaXguZG9jIiwgc2hvdy5jb25mLmludGVydmFsPVQpCmBgYAoKCmBgYHtyIG1lc3NhZ2U9RiwgZXZhbD1ULCBlY2hvPUYsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMH0KcGVubnljb29rX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwicGVubnljb29rX2FvdGVfZXhwMS5jc3YiKSkgJT4lCiAgY2xlYW5fbmFtZXMoKQoKY29ycmVsYXRpb246OmNvcnJlbGF0aW9uKHBlbm55Y29va19kYXRhKSAlPiUgc3VtbWFyeSgpICU+JQogIGtuaXRyOjprYWJsZShkaWdpdHMgPSAyKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gRiklPiUKICBzY3JvbGxfYm94KHdpZHRoID0gIjc4MHB4IikKCmBgYAoKCiMjIExpbmVhciBSZWdyZXNzaW9uCgpIZXJlLCB3ZSBkbyBzaW5nbGUgYW5kIG11bHRpcGxlIGxpbmVhciByZWdyZWFzc2lvbiBvbiB0aGUgYG5hcmNpc3Npc21fZGF0YWA6CgpgYGB7cn0KbTEgPC0gbG0obWVudGFsX2hlYWx0aH5uYXJjaXNzaXNtLCBkYXRhPSBuYXJjaXNzaXNtX2RhdGEpCmBgYAoKYGBge3IgbWVzc2FnZT1GLCBldmFsPVQsIGVjaG89RiwgZmlnLmFsaWduPSdjZW50ZXInLCBkcGk9MzAwfQpicm9vbTo6dGlkeShtMSklPiUKICBrbml0cjo6a2FibGUoZGlnaXRzID0gMikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCmBgYAoKYGBge3J9Cm0yIDwtIGxtKG1lbnRhbF9oZWFsdGh+bmFyY2lzc2lzbStwc3ljaG9wYXRoeSwgZGF0YT0gbmFyY2lzc2lzbV9kYXRhKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RiwgZXZhbD1ULCBlY2hvPUYsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMH0KYnJvb206OnRpZHkobTIpJT4lCiAga25pdHI6OmthYmxlKGRpZ2l0cyA9IDIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBUKQpgYGAKCiogKkV4ZXJjaXNlKjogVHLDqW1vbGnDqHJlIGFuZCBEamVyaW91YXQgKDIwMjApIGV4YW1pbmVkIHRoZSByb2xlIG9mICpjb2duaXRpdmUgcmVmbGVjdGlvbiogYW5kICpiZWxpZWYgaW4gc2NpZW5jZSogaW4gY2xpbWF0ZSBjaGFuZ2Ugc2tlcHRpY2lzbS4gSW4gdGhlaXIgZmlyc3Qgc3R1ZHksIHRoZXkgcmV2ZWFsZWQgdGhhdCBjb2duaXRpdmUgcmVmbGVjdGlvbiBhbmQgYmVsaWVmIGluIHNjaWVuY2UgbmVnZXRpdmVseSBwcmVkaWN0ZWQgY2xpbWF0ZSBjaGFuZ2Ugc2tlcHRpY2lzbSBldmVuIGFmdGVyIGNvbnRyb2xsaW5nIGZvciBkZW1vZ3JhcGhpYyBhbmQgY29nbml0aXZlIGFiaWxpdHkgdmFyaWFibGVzIChzZWUgdGhlIG9yaWdpbmFsIHBhcGVyIFtoZXJlXShodHRwczovL3BzeWFyeGl2LmNvbS92cDhrNi8pKS4gCgpgYGB7ciBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjcwMHB4Iiwgb3V0LmhlaWdodD0iMzUwcHgiLCBmaWcuY2FwPSAiYWRhcHRlZCBmcm9tIFtUcsOpbW9sacOocmUgYW5kIERqZXJpb3VhdCAoMjAyMCldKGh0dHBzOi8vcHN5YXJ4aXYuY29tL3ZwOGs2LykifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCd0cmVtb2xpZXJlX3JlZy5wbmcnKSkKYGBgCgpPcGVuIHRoZSBgdHJlbW9saWVyZV9kYXRhX2V4cDEuY3N2YCBmaWxlIGFuZCB0cnkgdG8gcmVwcm9kdWNlIHRoZWlyIHJlc3VsdHMgYnkgcnVubmluZyBhIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uLiBFbnRlciBhZ2UsIGdlbmRlciwgZWR1Y2F0aW9uLCBiZWxpZWYgaW4gc2NpZW5jZSwgbGl0ZXJhY3ksIG51bWVyYWN5IChOdW10b3RhbCksIGFuZCBjb2duaXRpdmUgcmVmbGVjdGlvbiBhcyBwcmVkaWN0b3JzIGFuZCBlbnRlciBjbGltYXRlIGNoYW5nZSBza2VwdGljaXNtIChjbGltYXRvKSBhcyB0aGUgb3V0Y29tZSB2YXJpYWJsZS4KCmBgYHtyIG1lc3NhZ2U9Rn0KVHJlbW9saWVyZV9kYXRhIDwtIHJlYWRfY3N2KGhlcmUoImNsZWFuZWRfZGF0YSIsInRyZW1vbGllcmVfZGF0YV9leHAxLmNzdiIpKQoKVHJlbW9saWVyZV9yZWc9bG0oQ2xpbWF0byB+IEFnZSsgR2VuZGVyKyBFZHVjYXRpb24rIEJlbGllZkluU2NpZW5jZXRvdGFsKyBMaXRlcmFjeSsgTnVtdG90YWwrIENvZ25pdGl2ZVJlZmxlY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgZGF0YT1UcmVtb2xpZXJlX2RhdGEpCmBgYAoKCmBgYHtyIG1lc3NhZ2U9RiwgZXZhbD1ULCBlY2hvPUYsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMH0KYnJvb206OnRpZHkoVHJlbW9saWVyZV9yZWcpJT4lCiAga25pdHI6OmthYmxlKGRpZ2l0cyA9IDIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBUKQoKZ2xhbmNlKFRyZW1vbGllcmVfcmVnKSU+JQogIGtuaXRyOjprYWJsZShkaWdpdHMgPSAyKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gRiklPiUKICBzY3JvbGxfYm94KHdpZHRoID0gIjc4MHB4IikKYGBgCgoKIyBSbWFya2Rvd24KClRvIGJlIGNvbXBsZXRlZC4uLgoKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJBcnR3b3JrIGJ5IEFsbGlzb24gSG9yc3Q6IGh0dHBzOi8vZ2l0aHViLmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucyJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ3JtYXJrZG93bl93aXphcmRzLnBuZycpKQpgYGAKCgpgYGB7ciBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjcwMHB4Iiwgb3V0LmhlaWdodD0iMzUwcHgiLCBmaWcuY2FwPSAiQXJ0d29yayBieSBBbGxpc29uIEhvcnN0OiBodHRwczovL2dpdGh1Yi5jb20vYWxsaXNvbmhvcnN0L3N0YXRzLWlsbHVzdHJhdGlvbnMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCdyZXByb2R1Y2liaWxpdHlfY291cnQucG5nJykpCmBgYAoKIyBSZWZlcmVuY2VzCgotIEdoYXNlbWksIE8uLCBIYW5kbGV5LCBTLiwgJiBIb3dhcnRoLCBTLiAoMjAyMCkuIFRoZSBCcmlnaHQgSG9tdW5jdWx1cyBpbiBvdXIgSGVhZDogSW5kaXZpZHVhbCBEaWZmZXJlbmNlcyBpbiBJbnR1aXRpdmUgU2Vuc2l0aXZpdHkgdG8gTG9naWNhbCBWYWxpZGl0eS4KCi0gSm9obiwgTC4gSy4sIEplb25nLCBNLiwgR2lubywgRi4sICYgSHVhbmcsIEwuICgyMDE5KS4gVGhlIHNlbGYtcHJlc2VudGF0aW9uYWwgY29uc2VxdWVuY2VzIG9mIHVwaG9sZGluZyBvbmXigJlzIHN0YW5jZSBpbiBzcGl0ZSBvZiB0aGUgZXZpZGVuY2UuIE9yZ2FuaXphdGlvbmFsIEJlaGF2aW9yIGFuZCBIdW1hbiBEZWNpc2lvbiBQcm9jZXNzZXMsIDE1NCwgMS0xNC4KCi0gUGVubnljb29rLCBHLiwgQ2hleW5lLCBKLiBBLiwgS29laGxlciwgRC4gSi4sICYgRnVnZWxzYW5nLCBKLiBBLiAoMjAyMCkuIE9uIHRoZSBiZWxpZWYgdGhhdCBiZWxpZWZzIHNob3VsZCBjaGFuZ2UgYWNjb3JkaW5nIHRvIGV2aWRlbmNlOiBJbXBsaWNhdGlvbnMgZm9yIGNvbnNwaXJhdG9yaWFsLCBtb3JhbCwgcGFyYW5vcm1hbCwgcG9saXRpY2FsLCByZWxpZ2lvdXMsIGFuZCBzY2llbmNlIGJlbGllZnMuIEp1ZGdtZW50IGFuZCBEZWNpc2lvbiBNYWtpbmcsIDE1KDQpLCA0NzYuCgotIFJvdGVsbG8sIEMuIE0uLCBLZWxseSwgTC4gSi4sIEhlaXQsIEUuLCBWYXppcmUsIFMuLCAmIFZ1bCwgRS4gKDIwMTgpLiBUaGUgU2hhcGUgb2YgUk9DIEN1cnZlcyBpbiBTaG9vdGVyIFRhc2tzOiBJbXBsaWNhdGlvbnMgZm9yIEJlc3QgUHJhY3RpY2VzIGluIEFuYWx5c2lzLiBDb2xsYWJyYTogUHN5Y2hvbG9neSwgNCgxKS4KCi0gVHLDqW1vbGnDqHJlLCBCLiwgJiBEamVyaW91YXQsIEguICgyMDIwKS4gRG9u4oCZdCB5b3Ugc2VlIHRoYXQgaXRzIGNvbGQhIEV4cGxvcmluZyB0aGUgcm9sZXMgb2YgY29nbml0aXZlIHJlZmxlY3Rpb24sIGNsaW1hdGUgc2NpZW5jZSBsaXRlcmFjeSwgaWxsdXNpb24gb2Yga25vd2xlZGdlLCBhbmQgcG9saXRpY2FsIG9yaWVudGF0aW9uIGluIGNsaW1hdGUgY2hhbmdlIHNrZXB0aWNpc20uCgotIFdpY2toYW0sIEguICgyMDE0KS4gVGlkeSBkYXRhLiBKb3VybmFsIG9mIFN0YXRpc3RpY2FsIFNvZnR3YXJlLCA1OSgxMCksIDEtMjMu